summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk5
-rw-r--r--api/current.txt11
-rw-r--r--api/removed.txt4
-rw-r--r--api/system-current.txt14
-rw-r--r--api/system-removed.txt11
-rw-r--r--cmds/screencap/screencap.cpp97
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp3
-rw-r--r--cmds/statsd/src/StatsService.cpp2
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp28
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.h3
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp6
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp30
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h20
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp74
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp72
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h6
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp17
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp99
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h6
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp5
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h6
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp148
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h14
-rw-r--r--cmds/statsd/src/stats_log.proto7
-rw-r--r--cmds/statsd/tests/guardrail/StatsdStats_test.cpp200
-rw-r--r--core/java/android/app/ActivityManagerInternal.java5
-rw-r--r--core/java/android/app/ActivityThread.java437
-rw-r--r--core/java/android/app/ClientTransactionHandler.java114
-rw-r--r--core/java/android/app/ContextImpl.java15
-rw-r--r--core/java/android/app/IApplicationThread.aidl28
-rw-r--r--core/java/android/app/ProfilerInfo.java29
-rw-r--r--core/java/android/app/ResultInfo.java27
-rw-r--r--core/java/android/app/admin/ConnectEvent.java35
-rw-r--r--core/java/android/app/admin/DnsEvent.java51
-rw-r--r--core/java/android/app/admin/NetworkEvent.java31
-rw-r--r--core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java88
-rw-r--r--core/java/android/app/servertransaction/ActivityLifecycleItem.java44
-rw-r--r--core/java/android/app/servertransaction/ActivityResultItem.java95
-rw-r--r--core/java/android/app/servertransaction/BaseClientRequest.java45
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.aidl20
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java201
-rw-r--r--core/java/android/app/servertransaction/ClientTransactionItem.java54
-rw-r--r--core/java/android/app/servertransaction/ConfigurationChangeItem.java85
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java99
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java232
-rw-r--r--core/java/android/app/servertransaction/MoveToDisplayItem.java93
-rw-r--r--core/java/android/app/servertransaction/MultiWindowModeChangeItem.java92
-rw-r--r--core/java/android/app/servertransaction/NewIntentItem.java108
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java125
-rw-r--r--core/java/android/app/servertransaction/PipModeChangeItem.java89
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java114
-rw-r--r--core/java/android/app/servertransaction/StopActivityItem.java112
-rw-r--r--core/java/android/app/servertransaction/WindowVisibilityItem.java85
-rw-r--r--core/java/android/content/pm/ActivityInfo.java2
-rw-r--r--core/java/android/content/pm/InstantAppInfo.java2
-rw-r--r--core/java/android/content/pm/PackageParser.java10
-rw-r--r--core/java/android/database/OWNERS2
-rw-r--r--core/java/android/os/BatteryStats.java111
-rw-r--r--core/java/android/os/PowerManager.java5
-rw-r--r--core/java/android/os/UEventObserver.java2
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl2
-rw-r--r--core/java/android/os/storage/StorageManager.java9
-rw-r--r--core/java/android/provider/SearchIndexablesContract.java36
-rw-r--r--core/java/android/provider/SearchIndexablesProvider.java16
-rw-r--r--core/java/android/provider/Settings.java11
-rw-r--r--core/java/android/service/autofill/NegationValidator.java79
-rw-r--r--core/java/android/service/autofill/Validators.java13
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java3
-rw-r--r--core/java/android/view/DisplayCutout.java408
-rw-r--r--core/java/android/view/SurfaceControl.java20
-rw-r--r--core/java/android/view/View.java52
-rw-r--r--core/java/android/view/ViewGroup.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java2
-rw-r--r--core/java/android/view/WindowInsets.java50
-rw-r--r--core/java/android/view/WindowManager.java6
-rw-r--r--core/java/android/view/accessibility/AccessibilityCache.java7
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java117
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java33
-rw-r--r--core/java/com/android/internal/content/ReferrerIntent.java19
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java5
-rw-r--r--core/jni/android_view_Surface.cpp18
-rw-r--r--core/jni/android_view_SurfaceControl.cpp113
-rw-r--r--core/proto/android/os/batterystats.proto20
-rw-r--r--core/proto/android/providers/settings.proto3
-rw-r--r--core/res/res/values/attrs.xml6
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/public.xml3
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/styles_device_defaults.xml21
-rw-r--r--core/res/res/values/styles_material.xml2
-rw-r--r--core/res/res/values/themes_device_defaults.xml244
-rw-r--r--core/tests/coretests/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/AndroidTest.xml1
-rw-r--r--core/tests/coretests/BstatsTestApp/Android.mk32
-rw-r--r--core/tests/coretests/BstatsTestApp/AndroidManifest.xml28
-rw-r--r--core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java60
-rw-r--r--core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java83
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdCallback.aidl21
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdReceiver.aidl22
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java78
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java641
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--core/tests/coretests/src/android/view/DisplayCutoutTest.java347
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java)40
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java88
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/AccessibilityNodeInfoTest.java)19
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java133
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java399
-rw-r--r--keystore/java/android/security/KeyChain.java15
-rw-r--r--location/tests/Android.mk3
-rw-r--r--media/java/android/media/tv/TvInputManager.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SystemUI/res/values/config.xml5
-rw-r--r--packages/SystemUI/res/values/ids.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeHost.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java317
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java655
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java208
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java112
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java300
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java7
-rw-r--r--services/core/java/com/android/server/NetworkTimeUpdateService.java8
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java23
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java106
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java30
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java52
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java5
-rw-r--r--services/core/java/com/android/server/am/AppTaskImpl.java2
-rw-r--r--services/core/java/com/android/server/am/ClientLifecycleManager.java120
-rw-r--r--services/core/java/com/android/server/am/CompatModePackages.java20
-rw-r--r--services/core/java/com/android/server/am/LaunchingBoundsController.java8
-rw-r--r--services/core/java/com/android/server/am/RecentTasks.java46
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java11
-rw-r--r--services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java154
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java23
-rw-r--r--services/core/java/com/android/server/pm/Settings.java5
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java22
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java8
-rw-r--r--services/core/java/com/android/server/power/BatterySaverPolicy.java15
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java4
-rw-r--r--services/core/java/com/android/server/power/batterysaver/FileUpdater.java132
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java359
-rw-r--r--services/core/java/com/android/server/wm/DragState.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java21
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java28
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java35
-rw-r--r--services/core/jni/com_android_server_UsbDescriptorParser.cpp44
-rw-r--r--services/core/jni/com_android_server_UsbHostManager.cpp161
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java6
-rw-r--r--services/tests/servicestests/Android.mk1
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java258
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java252
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java108
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java189
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java69
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java21
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java1
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java242
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb10ACHeader.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb10ACInputTerminal.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb10ACOutputTerminal.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatI.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatII.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb10ASGeneral.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java12
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java16
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java6
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java52
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java56
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java112
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java66
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java56
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java54
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java6
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java15
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java6
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java14
-rw-r--r--test-base/Android.mk (renamed from legacy-test/Android.mk)6
-rw-r--r--test-base/api/android-test-base-current.txt (renamed from legacy-test/api/legacy-test-current.txt)0
-rw-r--r--test-base/api/android-test-base-removed.txt (renamed from legacy-test/api/legacy-test-removed.txt)0
-rw-r--r--test-base/api/apicheck_msg_android_test_base.txt (renamed from legacy-test/api/apicheck_msg_legacy_test.txt)0
-rw-r--r--test-base/jarjar-rules.txt (renamed from legacy-test/jarjar-rules.txt)0
-rw-r--r--test-base/src/android/test/AndroidTestCase.java (renamed from legacy-test/src/android/test/AndroidTestCase.java)0
-rw-r--r--test-base/src/android/test/FlakyTest.java (renamed from legacy-test/src/android/test/FlakyTest.java)0
-rw-r--r--test-base/src/android/test/InstrumentationTestCase.java (renamed from legacy-test/src/android/test/InstrumentationTestCase.java)0
-rw-r--r--test-base/src/android/test/InstrumentationTestSuite.java (renamed from legacy-test/src/android/test/InstrumentationTestSuite.java)0
-rw-r--r--test-base/src/android/test/PerformanceTestCase.java (renamed from legacy-test/src/android/test/PerformanceTestCase.java)0
-rw-r--r--test-base/src/android/test/RepetitiveTest.java (renamed from legacy-test/src/android/test/RepetitiveTest.java)0
-rw-r--r--test-base/src/android/test/UiThreadTest.java (renamed from legacy-test/src/android/test/UiThreadTest.java)0
-rw-r--r--test-base/src/android/test/package.html (renamed from legacy-test/src/android/test/package.html)0
-rw-r--r--test-base/src/android/test/suitebuilder/annotation/LargeTest.java (renamed from legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java)0
-rw-r--r--test-base/src/android/test/suitebuilder/annotation/MediumTest.java (renamed from legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java)0
-rw-r--r--test-base/src/android/test/suitebuilder/annotation/SmallTest.java (renamed from legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java)0
-rw-r--r--test-base/src/android/test/suitebuilder/annotation/Smoke.java (renamed from legacy-test/src/android/test/suitebuilder/annotation/Smoke.java)0
-rw-r--r--test-base/src/android/test/suitebuilder/annotation/Suppress.java (renamed from legacy-test/src/android/test/suitebuilder/annotation/Suppress.java)0
-rw-r--r--test-base/src/android/test/suitebuilder/annotation/package.html (renamed from legacy-test/src/android/test/suitebuilder/annotation/package.html)0
-rw-r--r--test-base/src/com/android/internal/util/Predicate.java (renamed from legacy-test/src/com/android/internal/util/Predicate.java)0
-rw-r--r--test-base/src/junit/MODULE_LICENSE_CPL (renamed from legacy-test/src/junit/MODULE_LICENSE_CPL)0
-rw-r--r--test-base/src/junit/README.android (renamed from legacy-test/src/junit/README.android)0
-rw-r--r--test-base/src/junit/cpl-v10.html (renamed from legacy-test/src/junit/cpl-v10.html)0
-rw-r--r--test-base/src/junit/framework/Assert.java (renamed from legacy-test/src/junit/framework/Assert.java)0
-rw-r--r--test-base/src/junit/framework/AssertionFailedError.java (renamed from legacy-test/src/junit/framework/AssertionFailedError.java)0
-rw-r--r--test-base/src/junit/framework/ComparisonCompactor.java (renamed from legacy-test/src/junit/framework/ComparisonCompactor.java)0
-rw-r--r--test-base/src/junit/framework/ComparisonFailure.java (renamed from legacy-test/src/junit/framework/ComparisonFailure.java)0
-rw-r--r--test-base/src/junit/framework/Protectable.java (renamed from legacy-test/src/junit/framework/Protectable.java)0
-rw-r--r--test-base/src/junit/framework/Test.java (renamed from legacy-test/src/junit/framework/Test.java)0
-rw-r--r--test-base/src/junit/framework/TestCase.java (renamed from legacy-test/src/junit/framework/TestCase.java)0
-rw-r--r--test-base/src/junit/framework/TestFailure.java (renamed from legacy-test/src/junit/framework/TestFailure.java)0
-rw-r--r--test-base/src/junit/framework/TestListener.java (renamed from legacy-test/src/junit/framework/TestListener.java)0
-rw-r--r--test-base/src/junit/framework/TestResult.java (renamed from legacy-test/src/junit/framework/TestResult.java)0
-rw-r--r--test-base/src/junit/framework/TestSuite.java (renamed from legacy-test/src/junit/framework/TestSuite.java)0
-rw-r--r--test-mock/Android.mk13
-rw-r--r--test-runner/Android.mk2
-rw-r--r--test-runner/api/android-test-runner-current.txt2
-rw-r--r--test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java4
-rw-r--r--tests/testables/src/android/testing/TestableResources.java3
-rw-r--r--wifi/java/android/net/wifi/BatchedScanResult.java2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl3
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java43
-rw-r--r--wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl36
-rw-r--r--wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java59
267 files changed, 9532 insertions, 2703 deletions
diff --git a/Android.mk b/Android.mk
index b1bb1bbe175e..7a3c46dc8e41 100644
--- a/Android.mk
+++ b/Android.mk
@@ -559,6 +559,7 @@ LOCAL_SRC_FILES += \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
+ wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl \
wifi/java/android/net/wifi/IWifiScanner.aidl \
wifi/java/android/net/wifi/IRttManager.aidl \
packages/services/PacProcessor/com/android/net/IProxyService.aidl \
@@ -914,7 +915,7 @@ packages_to_document := \
# Search through the base framework dirs for these packages.
# The result will be relative to frameworks/base.
fwbase_dirs_to_document := \
- legacy-test/src \
+ test-base/src \
$(patsubst $(LOCAL_PATH)/%,%, \
$(wildcard \
$(foreach dir, $(FRAMEWORKS_BASE_JAVA_SRC_DIRS), \
@@ -1058,7 +1059,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-since $(SRC_API_DIR)/25.txt 25 \
-since $(SRC_API_DIR)/26.txt 26 \
-since $(SRC_API_DIR)/27.txt 27 \
- -werror -lerror -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 \
+ -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-overview $(LOCAL_PATH)/core/java/overview.html \
framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
diff --git a/api/current.txt b/api/current.txt
index 93b6b424458f..8329aca26b2a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1119,6 +1119,7 @@ package android {
field public static final int scheme = 16842791; // 0x1010027
field public static final int screenDensity = 16843467; // 0x10102cb
field public static final int screenOrientation = 16842782; // 0x101001e
+ field public static final int screenReaderFocusable = 16844148; // 0x1010574
field public static final int screenSize = 16843466; // 0x10102ca
field public static final int scrollHorizontally = 16843099; // 0x101015b
field public static final int scrollIndicators = 16844006; // 0x10104e6
@@ -2313,7 +2314,9 @@ package android {
field public static final int Widget_DeviceDefault_AutoCompleteTextView = 16974151; // 0x1030147
field public static final int Widget_DeviceDefault_Button = 16974145; // 0x1030141
field public static final int Widget_DeviceDefault_Button_Borderless = 16974188; // 0x103016c
+ field public static final int Widget_DeviceDefault_Button_Borderless_Colored = 16974561; // 0x10302e1
field public static final int Widget_DeviceDefault_Button_Borderless_Small = 16974149; // 0x1030145
+ field public static final int Widget_DeviceDefault_Button_Colored = 16974560; // 0x10302e0
field public static final int Widget_DeviceDefault_Button_Inset = 16974147; // 0x1030143
field public static final int Widget_DeviceDefault_Button_Small = 16974146; // 0x1030142
field public static final int Widget_DeviceDefault_Button_Toggle = 16974148; // 0x1030144
@@ -6588,6 +6591,7 @@ package android.app.admin {
public abstract class NetworkEvent implements android.os.Parcelable {
method public int describeContents();
+ method public long getId();
method public java.lang.String getPackageName();
method public long getTimestamp();
field public static final android.os.Parcelable.Creator<android.app.admin.NetworkEvent> CREATOR;
@@ -37556,6 +37560,7 @@ package android.service.autofill {
public final class Validators {
method public static android.service.autofill.Validator and(android.service.autofill.Validator...);
+ method public static android.service.autofill.Validator not(android.service.autofill.Validator);
method public static android.service.autofill.Validator or(android.service.autofill.Validator...);
}
@@ -41769,8 +41774,6 @@ package android.test.suitebuilder {
public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
- method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
- method public final android.test.suitebuilder.TestSuiteBuilder addRequirements(com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>...);
method public final junit.framework.TestSuite build();
method public android.test.suitebuilder.TestSuiteBuilder excludePackages(java.lang.String...);
method protected java.lang.String getSuiteName();
@@ -46331,6 +46334,7 @@ package android.view {
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
+ method public boolean isScreenReaderFocusable();
method public boolean isScrollContainer();
method public boolean isScrollbarFadingEnabled();
method public boolean isSelected();
@@ -46550,6 +46554,7 @@ package android.view {
method public void setSaveFromParentEnabled(boolean);
method public void setScaleX(float);
method public void setScaleY(float);
+ method public void setScreenReaderFocusable(boolean);
method public void setScrollBarDefaultDelayBeforeFade(int);
method public void setScrollBarFadeDuration(int);
method public void setScrollBarSize(int);
@@ -47968,6 +47973,7 @@ package android.view.accessibility {
method public boolean isLongClickable();
method public boolean isMultiLine();
method public boolean isPassword();
+ method public boolean isScreenReaderFocusable();
method public boolean isScrollable();
method public boolean isSelected();
method public boolean isShowingHintText();
@@ -48023,6 +48029,7 @@ package android.view.accessibility {
method public void setParent(android.view.View, int);
method public void setPassword(boolean);
method public void setRangeInfo(android.view.accessibility.AccessibilityNodeInfo.RangeInfo);
+ method public void setScreenReaderFocusable(boolean);
method public void setScrollable(boolean);
method public void setSelected(boolean);
method public void setShowingHintText(boolean);
diff --git a/api/removed.txt b/api/removed.txt
index 2934846b480e..be4d5be1e1a3 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -210,10 +210,6 @@ package android.media {
package android.media.tv {
- public final class TvInputManager {
- method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
- }
-
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 990df9be9b29..95f28e4773cb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -751,6 +751,20 @@ package android.content.pm {
field public java.lang.String credentialProtectedDataDir;
}
+ public final class InstantAppInfo implements android.os.Parcelable {
+ ctor public InstantAppInfo(android.content.pm.ApplicationInfo, java.lang.String[], java.lang.String[]);
+ ctor public InstantAppInfo(java.lang.String, java.lang.CharSequence, java.lang.String[], java.lang.String[]);
+ method public int describeContents();
+ method public android.content.pm.ApplicationInfo getApplicationInfo();
+ method public java.lang.String[] getGrantedPermissions();
+ method public java.lang.String getPackageName();
+ method public java.lang.String[] getRequestedPermissions();
+ method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
+ method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppInfo> CREATOR;
+ }
+
public final class InstantAppIntentFilter implements android.os.Parcelable {
ctor public InstantAppIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>);
method public int describeContents();
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 396bcd640894..f98d011faf77 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -103,6 +103,10 @@ package android.content.pm {
package android.media.tv {
+ public final class TvInputManager {
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ }
+
public static final class TvInputManager.Hardware {
method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent);
}
@@ -111,6 +115,13 @@ package android.media.tv {
package android.net.wifi {
+ public deprecated class BatchedScanResult implements android.os.Parcelable {
+ ctor public BatchedScanResult();
+ ctor public BatchedScanResult(android.net.wifi.BatchedScanResult);
+ field public final java.util.List<android.net.wifi.ScanResult> scanResults;
+ field public boolean truncated;
+ }
+
public class ScanResult implements android.os.Parcelable {
field public boolean untrusted;
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6ded24648353..31722815276c 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -159,7 +159,7 @@ int main(int argc, char** argv)
void const* mapbase = MAP_FAILED;
ssize_t mapsize = -1;
- void const* base = NULL;
+ void* base = NULL;
uint32_t w, s, h, f;
android_dataspace d;
size_t size = 0;
@@ -179,7 +179,6 @@ int main(int argc, char** argv)
ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
- ScreenshotClient screenshot;
sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
if (display == NULL) {
fprintf(stderr, "Unable to get handle for display %d\n", displayId);
@@ -199,51 +198,57 @@ int main(int argc, char** argv)
uint8_t displayOrientation = configs[activeConfig].orientation;
uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
- status_t result = screenshot.update(display, Rect(),
- 0 /* reqWidth */, 0 /* reqHeight */,
- INT32_MIN, INT32_MAX, /* all layers */
- false, captureOrientation);
- if (result == NO_ERROR) {
- base = screenshot.getPixels();
- w = screenshot.getWidth();
- h = screenshot.getHeight();
- s = screenshot.getStride();
- f = screenshot.getFormat();
- d = screenshot.getDataSpace();
- size = screenshot.getSize();
+ sp<GraphicBuffer> outBuffer;
+ status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
+ 0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation,
+ &outBuffer);
+ if (result != NO_ERROR) {
+ close(fd);
+ _exit(1);
+ }
+
+ result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base);
+
+ if (base == NULL) {
+ close(fd);
+ _exit(1);
}
- if (base != NULL) {
- if (png) {
- const SkImageInfo info =
- SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType,
- dataSpaceToColorSpace(d));
- SkPixmap pixmap(info, base, s * bytesPerPixel(f));
- struct FDWStream final : public SkWStream {
- size_t fBytesWritten = 0;
- int fFd;
- FDWStream(int f) : fFd(f) {}
- size_t bytesWritten() const override { return fBytesWritten; }
- bool write(const void* buffer, size_t size) override {
- fBytesWritten += size;
- return size == 0 || ::write(fFd, buffer, size) > 0;
- }
- } fdStream(fd);
- (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
- if (fn != NULL) {
- notifyMediaScanner(fn);
- }
- } else {
- uint32_t c = dataSpaceToInt(d);
- write(fd, &w, 4);
- write(fd, &h, 4);
- write(fd, &f, 4);
- write(fd, &c, 4);
- size_t Bpp = bytesPerPixel(f);
- for (size_t y=0 ; y<h ; y++) {
- write(fd, base, w*Bpp);
- base = (void *)((char *)base + s*Bpp);
- }
+ w = outBuffer->getWidth();
+ h = outBuffer->getHeight();
+ s = outBuffer->getStride();
+ f = outBuffer->getPixelFormat();
+ d = HAL_DATASPACE_UNKNOWN;
+ size = s * h * bytesPerPixel(f);
+
+ if (png) {
+ const SkImageInfo info =
+ SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, dataSpaceToColorSpace(d));
+ SkPixmap pixmap(info, base, s * bytesPerPixel(f));
+ struct FDWStream final : public SkWStream {
+ size_t fBytesWritten = 0;
+ int fFd;
+ FDWStream(int f) : fFd(f) {}
+ size_t bytesWritten() const override { return fBytesWritten; }
+ bool write(const void* buffer, size_t size) override {
+ fBytesWritten += size;
+ return size == 0 || ::write(fFd, buffer, size) > 0;
+ }
+ } fdStream(fd);
+ (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
+ if (fn != NULL) {
+ notifyMediaScanner(fn);
+ }
+ } else {
+ uint32_t c = dataSpaceToInt(d);
+ write(fd, &w, 4);
+ write(fd, &h, 4);
+ write(fd, &f, 4);
+ write(fd, &c, 4);
+ size_t Bpp = bytesPerPixel(f);
+ for (size_t y=0 ; y<h ; y++) {
+ write(fd, base, w*Bpp);
+ base = (void *)((char *)base + s*Bpp);
}
}
close(fd);
@@ -253,4 +258,4 @@ int main(int argc, char** argv)
// b/36066697: Avoid running static destructors.
_exit(0);
-}
+} \ No newline at end of file
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 4779bc0b55af..57b4fc1e24b3 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -199,6 +199,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outD
iter.rp()->move(toRead);
}
}
+ StatsdStats::getInstance().noteMetricsReportSent(key);
}
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
@@ -234,7 +235,7 @@ void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs,
} else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
// We ignore the return value so we force each metric producer to clear its contents.
metricsManager->onDumpReport();
- StatsdStats::getInstance().noteDataDrop(key);
+ StatsdStats::getInstance().noteDataDropped(key);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
}
}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 31405b8eba33..618e03d655da 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -488,7 +488,7 @@ status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) {
if (args.size() > 1) {
reset = strtol(args[1].string(), NULL, 10);
}
- vector<int8_t> output;
+ vector<uint8_t> output;
statsdStats.dumpStats(&output, reset);
return NO_ERROR;
}
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index c2bf233c103b..7bacb441ee48 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -19,6 +19,9 @@
#include "AnomalyTracker.h"
+#include <android/os/IIncidentManager.h>
+#include <android/os/IncidentReportArgs.h>
+#include <binder/IServiceManager.h>
#include <time.h>
namespace android {
@@ -213,7 +216,7 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) {
// TODO: Can construct a name based on the criteria (and/or relay the criteria).
ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
}
- // TODO: informIncidentd();
+ informIncidentd();
} else {
ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
}
@@ -314,6 +317,29 @@ void AnomalyTracker::informAlarmsFired(const uint64_t& timestampNs,
}
}
+void AnomalyTracker::informIncidentd() {
+ VLOG("informIncidentd called.");
+ if (!mAlert.has_incidentd_details()) {
+ ALOGE("Attempted to call incidentd without any incidentd_details.");
+ return;
+ }
+ sp<IIncidentManager> service = interface_cast<IIncidentManager>(
+ defaultServiceManager()->getService(android::String16("incident")));
+ if (service == NULL) {
+ ALOGW("Couldn't get the incident service.");
+ return;
+ }
+
+ IncidentReportArgs incidentReport;
+ const Alert::IncidentdDetails& details = mAlert.incidentd_details();
+ for (int i = 0; i < details.section_size(); i++) {
+ incidentReport.addSection(details.section(i));
+ }
+ // TODO: Pass in mAlert.name() into the addHeader?
+
+ service->reportIncident(incidentReport);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index afa6fee99c6b..49e83235f73b 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -148,6 +148,9 @@ protected:
// Resets all bucket data. For use when all the data gets stale.
void resetStorage();
+ // Informs the incident service that an anomaly has occurred.
+ void informIncidentd();
+
FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index ac2192cdb3c5..445ee59947e9 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -210,6 +210,9 @@ StatsdConfig build_fake_config() {
alert->set_number_of_buckets(6);
alert->set_trigger_if_sum_gt(10);
alert->set_refractory_period_secs(30);
+ Alert::IncidentdDetails* details = alert->mutable_incidentd_details();
+ details->add_section(12);
+ details->add_section(13);
// Count process state changes, slice by uid.
metric = config.add_count_metric();
@@ -226,6 +229,9 @@ StatsdConfig build_fake_config() {
alert->set_number_of_buckets(4);
alert->set_trigger_if_sum_gt(30);
alert->set_refractory_period_secs(20);
+ details = alert->mutable_incidentd_details();
+ details->add_section(14);
+ details->add_section(15);
// Count process state changes, slice by uid, while SCREEN_IS_OFF
metric = config.add_count_metric();
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 815e03f6d3f1..2ab1146208ee 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -61,7 +61,7 @@ const int FIELD_ID_ATOM_STATS_COUNT = 2;
// TODO: add stats for pulled atoms.
StatsdStats::StatsdStats() {
mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
- mStartTime = time(nullptr);
+ mStartTimeSec = time(nullptr);
}
StatsdStats& StatsdStats::getInstance() {
@@ -107,6 +107,7 @@ void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) {
mMetricsStats.erase(key);
mConditionStats.erase(key);
mIceBox.push_back(it->second);
+ mConfigStats.erase(it);
}
}
@@ -126,7 +127,7 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
it->second.add_broadcast_sent_time_sec(time(nullptr));
}
-void StatsdStats::noteDataDrop(const ConfigKey& key) {
+void StatsdStats::noteDataDropped(const ConfigKey& key) {
lock_guard<std::mutex> lock(mLock);
auto it = mConfigStats.find(key);
if (it == mConfigStats.end()) {
@@ -137,6 +138,17 @@ void StatsdStats::noteDataDrop(const ConfigKey& key) {
it->second.add_data_drop_time_sec(time(nullptr));
}
+void StatsdStats::noteMetricsReportSent(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+
+ it->second.add_dump_report_time_sec(time(nullptr));
+}
+
void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) {
lock_guard<std::mutex> lock(mLock);
// if name doesn't exist before, it will create the key with count 0.
@@ -164,7 +176,7 @@ void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) {
void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
lock_guard<std::mutex> lock(mLock);
- if (timeSec < mStartTime) {
+ if (timeSec < mStartTimeSec) {
return;
}
@@ -183,7 +195,7 @@ void StatsdStats::reset() {
void StatsdStats::resetInternalLocked() {
// Reset the historical data, but keep the active ConfigStats
- mStartTime = time(nullptr);
+ mStartTimeSec = time(nullptr);
mIceBox.clear();
mConditionStats.clear();
mMetricsStats.clear();
@@ -225,11 +237,11 @@ void StatsdStats::addSubStatsToConfig(const ConfigKey& key,
}
}
-void StatsdStats::dumpStats(std::vector<int8_t>* output, bool reset) {
+void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
lock_guard<std::mutex> lock(mLock);
if (DEBUG) {
- time_t t = time(nullptr);
+ time_t t = mStartTimeSec;
struct tm* tm = localtime(&t);
char timeBuffer[80];
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm);
@@ -237,7 +249,7 @@ void StatsdStats::dumpStats(std::vector<int8_t>* output, bool reset) {
VLOG("Stats collection start second: %s", timeBuffer);
}
ProtoOutputStream proto;
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTime);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr));
VLOG("%lu Config in icebox: ", (unsigned long)mIceBox.size());
@@ -286,6 +298,10 @@ void StatsdStats::dumpStats(std::vector<int8_t>* output, bool reset) {
for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
VLOG("\tdata drop time: %d", dataDropTime);
}
+
+ for (const auto& dumpTime : configStats.dump_report_time_sec()) {
+ VLOG("\tdump report time: %d", dumpTime);
+ }
}
addSubStatsToConfig(pair.first, configStats);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 73ce2798cd6c..6fd9e4b6cd33 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -18,6 +18,7 @@
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include <gtest/gtest_prod.h>
#include <mutex>
#include <string>
#include <vector>
@@ -63,7 +64,14 @@ public:
/**
* Report a config's metrics data has been dropped.
*/
- void noteDataDrop(const ConfigKey& key);
+ void noteDataDropped(const ConfigKey& key);
+
+ /**
+ * Report metrics data report has been sent.
+ *
+ * The report may be requested via StatsManager API, or through adb cmd.
+ */
+ void noteMetricsReportSent(const ConfigKey& key);
/**
* Report the size of output tuple of a condition.
@@ -114,14 +122,14 @@ public:
*
* [reset]: whether to clear the historical stats after the call.
*/
- void dumpStats(std::vector<int8_t>* buffer, bool reset);
+ void dumpStats(std::vector<uint8_t>* buffer, bool reset);
private:
StatsdStats();
mutable std::mutex mLock;
- int32_t mStartTime;
+ int32_t mStartTimeSec;
// The stats about the configs that are still in use.
std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats;
@@ -153,6 +161,12 @@ private:
void resetInternalLocked();
void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats);
+
+ FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
+ FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
+ FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
+ FRIEND_TEST(StatsdStatsTest, TestSubStats);
+ FRIEND_TEST(StatsdStatsTest, TestAtomLog);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 00642400b3e8..4338399bf675 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -94,6 +94,8 @@ CountMetricProducer::~CountMetricProducer() {
}
void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+
mProto = std::make_unique<ProtoOutputStream>();
mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -107,13 +109,8 @@ void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
-std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
- long long endTime = time(nullptr) * NS_PER_SEC;
-
- // Dump current bucket if it's stale.
- // If current bucket is still on-going, don't force dump current bucket.
- // In finish(), We can force dump current bucket.
- flushIfNeeded(endTime);
+void CountMetricProducer::serializeBuckets() {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& counter : mPastBuckets) {
@@ -159,28 +156,40 @@ std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
}
mProto->end(wrapperToken);
}
-
mProto->end(mProtoToken);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
+ mPastBuckets.clear();
+ // TODO: Clear mDimensionKeyMap once the report is dumped.
+}
+
+std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
+ long long endTime = time(nullptr) * NS_PER_SEC;
VLOG("metric %s dump report now...", mMetric.name().c_str());
+ // Dump current bucket if it's stale.
+ // If current bucket is still on-going, don't force dump current bucket.
+ // In finish(), We can force dump current bucket.
+ flushIfNeeded(endTime);
+
+ // TODO(yanglu): merge these three functions to one to avoid three locks.
+ serializeBuckets();
+
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
- mPastBuckets.clear();
return buffer;
-
- // TODO: Clear mDimensionKeyMap once the report is dumped.
}
void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
mCondition = conditionMet;
}
bool CountMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
return false;
}
@@ -208,38 +217,40 @@ void CountMetricProducer::onMatchedLogEventInternal(
flushIfNeeded(eventTimeNs);
- if (condition == false) {
+ // ===========GuardRail==============
+ if (hitGuardRail(eventKey)) {
return;
}
- auto it = mCurrentSlicedCounter->find(eventKey);
-
- if (it == mCurrentSlicedCounter->end()) {
- // ===========GuardRail==============
- if (hitGuardRail(eventKey)) {
+ // TODO(yanglu): move the following logic to a seperate function to make it lockable.
+ {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+ if (condition == false) {
return;
}
- // create a counter for the new key
- (*mCurrentSlicedCounter)[eventKey] = 1;
- } else {
- // increment the existing value
- auto& count = it->second;
- count++;
- }
-
- for (auto& tracker : mAnomalyTrackers) {
- tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
- mCurrentSlicedCounter->find(eventKey)->second);
+ auto it = mCurrentSlicedCounter->find(eventKey);
+ if (it == mCurrentSlicedCounter->end()) {
+ // create a counter for the new key
+ mCurrentSlicedCounter->insert({eventKey, 1});
+ } else {
+ // increment the existing value
+ auto& count = it->second;
+ count++;
+ }
+ const int64_t& count = mCurrentSlicedCounter->find(eventKey)->second;
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, count);
+ }
+ VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(), (long long)(count));
}
-
- VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(),
- (long long)(*mCurrentSlicedCounter)[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+
if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return;
}
@@ -273,6 +284,7 @@ void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
// greater than actual data size as it contains each dimension of
// CountMetricData is duplicated.
size_t CountMetricProducer::byteSize() const {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index f78a199de103..164dfb25c01e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -75,6 +75,8 @@ protected:
void startNewProtoOutputStream(long long timestamp) override;
private:
+ void serializeBuckets();
+
const CountMetric mMetric;
// TODO: Add a lock to mPastBuckets.
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index a0374c0ba67c..3b49b9aadcde 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -104,6 +104,7 @@ DurationMetricProducer::~DurationMetricProducer() {
}
void DurationMetricProducer::startNewProtoOutputStream(long long startTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
mProto = std::make_unique<ProtoOutputStream>();
mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -111,7 +112,7 @@ void DurationMetricProducer::startNewProtoOutputStream(long long startTime) {
}
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
- const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
+ const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const {
switch (mMetric.aggregation_type()) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
@@ -130,6 +131,7 @@ void DurationMetricProducer::finish() {
}
void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
flushIfNeeded(eventTime);
// Now for each of the on-going event, check if the condition has changed for them.
@@ -139,6 +141,7 @@ void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime
}
void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
flushIfNeeded(eventTime);
@@ -149,15 +152,8 @@ void DurationMetricProducer::onConditionChanged(const bool conditionMet, const u
}
}
-std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
- long long endTime = time(nullptr) * NS_PER_SEC;
-
- // Dump current bucket if it's stale.
- // If current bucket is still on-going, don't force dump current bucket.
- // In finish(), We can force dump current bucket.
- flushIfNeeded(endTime);
- VLOG("metric %s dump report now...", mMetric.name().c_str());
-
+void DurationMetricProducer::SerializeBuckets() {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
VLOG(" dimension key %s", hashableKey.c_str());
@@ -214,13 +210,29 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
mProto->end(mProtoToken);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
+}
+
+std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
+
+ long long endTime = time(nullptr) * NS_PER_SEC;
+
+ // Dump current bucket if it's stale.
+ // If current bucket is still on-going, don't force dump current bucket.
+ // In finish(), We can force dump current bucket.
+ flushIfNeeded(endTime);
+
+ SerializeBuckets();
+
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
+
startNewProtoOutputStream(endTime);
// TODO: Properly clear the old buckets.
return buffer;
}
void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
return;
}
@@ -240,6 +252,7 @@ void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
}
bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
// the key is not new, we are good.
if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
return false;
@@ -265,32 +278,37 @@ void DurationMetricProducer::onMatchedLogEventInternal(
const LogEvent& event, bool scheduledPull) {
flushIfNeeded(event.GetTimestampNs());
- if (matcherIndex == mStopAllIndex) {
- for (auto& pair : mCurrentSlicedDuration) {
- pair.second->noteStopAll(event.GetTimestampNs());
+ // TODO(yanglu): move the following logic to a seperate function to make it lockable.
+ {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+ if (matcherIndex == mStopAllIndex) {
+ for (auto& pair : mCurrentSlicedDuration) {
+ pair.second->noteStopAll(event.GetTimestampNs());
+ }
+ return;
}
- return;
- }
- HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
+ HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
- if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
- if (hitGuardRail(eventKey)) {
- return;
+ if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+ if (hitGuardRail(eventKey)) {
+ return;
+ }
+ mCurrentSlicedDuration[eventKey] =
+ createDurationTracker(eventKey, mPastBuckets[eventKey]);
}
- mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
- }
+ auto it = mCurrentSlicedDuration.find(eventKey);
- auto it = mCurrentSlicedDuration.find(eventKey);
-
- if (matcherIndex == mStartIndex) {
- it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
- } else if (matcherIndex == mStopIndex) {
- it->second->noteStop(atomKey, event.GetTimestampNs(), false);
+ if (matcherIndex == mStartIndex) {
+ it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
+ } else if (matcherIndex == mStopIndex) {
+ it->second->noteStop(atomKey, event.GetTimestampNs(), false);
+ }
}
}
size_t DurationMetricProducer::byteSize() const {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 5b5373ec9aeb..68fff484b64d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -71,6 +71,8 @@ protected:
void startNewProtoOutputStream(long long timestamp) override;
private:
+ void SerializeBuckets();
+
const DurationMetric mMetric;
// Index of the SimpleLogEntryMatcher which defines the start.
@@ -96,8 +98,8 @@ private:
std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDuration;
- std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
- std::vector<DurationBucket>& bucket);
+ std::unique_ptr<DurationTracker> createDurationTracker(
+ const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const;
bool hitGuardRail(const HashableDimensionKey& newKey);
static const size_t kBucketSize = sizeof(DurationBucket{});
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 95a18f7241f0..53f112a507ce 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -73,6 +73,7 @@ EventMetricProducer::~EventMetricProducer() {
}
void EventMetricProducer::startNewProtoOutputStream(long long startTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
mProto = std::make_unique<ProtoOutputStream>();
// TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData,
// and StatsEvent.
@@ -89,11 +90,16 @@ void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
- mProto->end(mProtoToken);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
+ // TODO(yanglu): make this section to an util function.
+ {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+ mProto->end(mProtoToken);
+ mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
+
+ size_t bufferSize = mProto->size();
+ VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
+ }
- size_t bufferSize = mProto->size();
- VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
@@ -103,6 +109,7 @@ std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() {
void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
mCondition = conditionMet;
}
@@ -110,6 +117,7 @@ void EventMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
const LogEvent& event, bool scheduledPull) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
if (!condition) {
return;
}
@@ -124,6 +132,7 @@ void EventMetricProducer::onMatchedLogEventInternal(
}
size_t EventMetricProducer::byteSize() const {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
return mProto->bytesWritten();
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1791654ba7cc..ed4c76083303 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -102,6 +102,7 @@ GaugeMetricProducer::~GaugeMetricProducer() {
}
void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
mProto = std::make_unique<ProtoOutputStream>();
mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -111,14 +112,8 @@ void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) {
void GaugeMetricProducer::finish() {
}
-std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
- VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
-
- // Dump current bucket if it's stale.
- // If current bucket is still on-going, don't force dump current bucket.
- // In finish(), We can force dump current bucket.
- flushIfNeeded(time(nullptr) * NS_PER_SEC);
-
+void GaugeMetricProducer::SerializeBuckets() {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
auto it = mDimensionKeyMap.find(hashableKey);
@@ -166,50 +161,69 @@ std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
mProto->end(mProtoToken);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
+ mPastBuckets.clear();
+}
+
+std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
+ VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
+
+ // Dump current bucket if it's stale.
+ // If current bucket is still on-going, don't force dump current bucket.
+ // In finish(), We can force dump current bucket.
+ flushIfNeeded(time(nullptr) * NS_PER_SEC);
+
+ SerializeBuckets();
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
- mPastBuckets.clear();
-
return buffer;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
- AutoMutex _l(mLock);
VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+
+ // flushIfNeeded holds the write lock and is thread-safe.
flushIfNeeded(eventTime);
- mCondition = conditionMet;
- // Push mode. No need to proactively pull the gauge data.
- if (mPullTagId == -1) {
- return;
- }
- if (!mCondition) {
- return;
- }
- // Already have gauge metric for the current bucket, do not do it again.
- if (mCurrentSlicedBucket->size() > 0) {
- return;
- }
vector<std::shared_ptr<LogEvent>> allData;
- if (!mStatsPullerManager.Pull(mPullTagId, &allData)) {
- ALOGE("Stats puller failed for tag: %d", mPullTagId);
- return;
+ // The following section is to update the condition and re-pull the gauge.
+ // TODO(yanglu): make it a seperate lockable function.
+ {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+
+ mCondition = conditionMet;
+
+ // Push mode. No need to proactively pull the gauge data.
+ if (mPullTagId == -1) {
+ return;
+ }
+ if (!mCondition) {
+ return;
+ }
+ // Already have gauge metric for the current bucket, do not do it again.
+ if (mCurrentSlicedBucket->size() > 0) {
+ return;
+ }
+ if (!mStatsPullerManager.Pull(mPullTagId, &allData)) {
+ ALOGE("Stats puller failed for tag: %d", mPullTagId);
+ return;
+ }
}
+
+ // onMatchedLogEventInternal holds the write lock and is thread-safe.
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, false /*scheduledPull*/);
}
- flushIfNeeded(eventTime);
}
void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
-int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
+int64_t GaugeMetricProducer::getGauge(const LogEvent& event) const {
status_t err = NO_ERROR;
int64_t val = event.GetLong(mMetric.gauge_field(), &err);
if (err == NO_ERROR) {
@@ -221,13 +235,14 @@ int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
}
void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
- AutoMutex mutex(mLock);
+ // onMatchedLogEventInternal holds the write lock and is thread-safe.
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, true /*scheduledPull*/);
}
}
bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
return false;
}
@@ -251,32 +266,32 @@ void GaugeMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
const LogEvent& event, bool scheduledPull) {
+ uint64_t eventTimeNs = event.GetTimestampNs();
+ flushIfNeeded(eventTimeNs);
+
if (condition == false) {
return;
}
- uint64_t eventTimeNs = event.GetTimestampNs();
+ const long gauge = getGauge(event);
+ if (gauge < 0) {
+ return;
+ }
+ if (hitGuardRail(eventKey)) {
+ return;
+ }
+
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
- // When the event happens in a new bucket, flush the old buckets.
- if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) {
- flushIfNeeded(eventTimeNs);
- }
-
// For gauge metric, we just simply use the first gauge in the given bucket.
if (!mCurrentSlicedBucket->empty()) {
return;
}
- const long gauge = getGauge(event);
- if (gauge >= 0) {
- if (hitGuardRail(eventKey)) {
- return;
- }
(*mCurrentSlicedBucket)[eventKey] = gauge;
- }
for (auto& tracker : mAnomalyTrackers) {
tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge);
}
@@ -288,6 +303,7 @@ void GaugeMetricProducer::onMatchedLogEventInternal(
// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
// the GaugeMetricProducer while holding the lock.
void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return;
}
@@ -321,6 +337,7 @@ void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
}
size_t GaugeMetricProducer::byteSize() const {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index f344303179e8..8df61117d75d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -81,6 +81,8 @@ protected:
void startNewProtoOutputStream(long long timestamp) override;
private:
+ void SerializeBuckets();
+
// The default bucket size for gauge metric is 1 second.
static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
const GaugeMetric mMetric;
@@ -89,8 +91,6 @@ private:
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
- Mutex mLock;
-
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
@@ -98,7 +98,7 @@ private:
// The current bucket.
std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>();
- int64_t getGauge(const LogEvent& event);
+ int64_t getGauge(const LogEvent& event) const;
bool hitGuardRail(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 62fb632faaaf..7542a9439032 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -23,6 +23,7 @@ using std::map;
void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event,
bool scheduledPull) {
+ std::unique_lock<std::shared_timed_mutex> writeLock(mRWMutex);
uint64_t eventTimeNs = event.GetTimestampNs();
// this is old event, maybe statsd restarted?
if (eventTimeNs < mStartTimeNs) {
@@ -59,12 +60,16 @@ void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent
} else {
condition = mCondition;
}
+ // Unlock as onMatchedLogEventInternal is threadsafe.
+ writeLock.unlock();
onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event,
scheduledPull);
}
std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+
size_t bufferSize = mProto->size();
std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index b22ff6f3348c..27343ad6215a 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,6 +17,8 @@
#ifndef METRIC_PRODUCER_H
#define METRIC_PRODUCER_H
+#include <shared_mutex>
+
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "config/ConfigKey.h"
@@ -137,6 +139,10 @@ protected:
long long mProtoToken;
+ // Read/Write mutex to make the producer thread-safe.
+ // TODO(yanglu): replace with std::shared_mutex when available in libc++.
+ mutable std::shared_timed_mutex mRWMutex;
+
virtual void startNewProtoOutputStream(long long timestamp) = 0;
std::unique_ptr<std::vector<uint8_t>> serializeProto();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 66c8419de662..eed7841ab435 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -121,6 +121,7 @@ ValueMetricProducer::~ValueMetricProducer() {
}
void ValueMetricProducer::startNewProtoOutputStream(long long startTime) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
mProto = std::make_unique<ProtoOutputStream>();
mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
@@ -136,9 +137,8 @@ void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
-std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
- VLOG("metric %s dump report now...", mMetric.name().c_str());
-
+void ValueMetricProducer::SerializeBuckets() {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
VLOG(" dimension key %s", hashableKey.c_str());
@@ -185,47 +185,63 @@ std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
mProto->end(mProtoToken);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
+ mPastBuckets.clear();
+}
+std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
VLOG("metric %s dump report now...", mMetric.name().c_str());
+
+ SerializeBuckets();
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
- mPastBuckets.clear();
return buffer;
-
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
- AutoMutex _l(mLock);
- mCondition = condition;
+ vector<shared_ptr<LogEvent>> allData;
+
+ // TODO(yanglu): move the following logic to a seperate function to make it lockable.
+ {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+ mCondition = condition;
+
+ if (mPullTagId == -1) {
+ return;
+ }
- if (mPullTagId != -1) {
if (mCondition == true) {
mStatsPullerManager->RegisterReceiver(mPullTagId, this,
mMetric.bucket().bucket_size_millis());
} else if (mCondition == false) {
mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
}
-
- vector<shared_ptr<LogEvent>> allData;
- if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
- if (allData.size() == 0) {
- return;
- }
- for (const auto& data : allData) {
- onMatchedLogEvent(0, *data, false);
- }
- flushIfNeeded(eventTime);
+ if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
+ return;
}
+ }
+
+ if (allData.size() == 0) {
return;
}
+
+ // onMatchedLogEventInternal holds the write lock and is thread-safe.
+ for (const auto& data : allData) {
+ onMatchedLogEvent(0, *data, false);
+ }
+ // flushIfNeeded holds the write lock and is thread-safe.
+ flushIfNeeded(eventTime);
+}
+
+bool ValueMetricProducer::IsConditionMet() const {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
+ return mCondition == true || !mMetric.has_condition();
}
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
- AutoMutex _l(mLock);
- if (mCondition == true || !mMetric.has_condition()) {
+ if (IsConditionMet()) {
if (allData.size() == 0) {
return;
}
@@ -242,6 +258,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
}
bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -262,58 +279,75 @@ bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
return false;
}
-void ValueMetricProducer::onMatchedLogEventInternal(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const map<string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) {
- uint64_t eventTimeNs = event.GetTimestampNs();
+void ValueMetricProducer::onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs,
+ const HashableDimensionKey& eventKey,
+ const long& value, bool scheduledPull) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
+ Interval& interval = mCurrentSlicedBucket[eventKey];
+ if (scheduledPull) {
+ // scheduled pull always sets beginning of current bucket and end
+ // of next bucket
+ if (interval.raw.size() > 0) {
+ interval.raw.back().second = value;
+ } else {
+ interval.raw.push_back(make_pair(value, value));
+ }
+ Interval& nextInterval = mNextSlicedBucket[eventKey];
+ if (nextInterval.raw.size() == 0) {
+ nextInterval.raw.push_back(make_pair(value, 0));
+ } else {
+ nextInterval.raw.front().first = value;
+ }
+ } else {
+ if (mCondition == true) {
+ interval.raw.push_back(make_pair(value, 0));
+ } else {
+ if (interval.raw.size() != 0) {
+ interval.raw.back().second = value;
+ } else {
+ interval.tainted = true;
+ VLOG("Data on condition true missing!");
+ }
+ }
+ }
+}
- if (hitGuardRail(eventKey)) {
+void ValueMetricProducer::onMatchedLogEventInternal_push(const uint64_t& eventTimeNs,
+ const HashableDimensionKey& eventKey,
+ const long& value) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+ (long long)mCurrentBucketStartTimeNs);
return;
}
- Interval& interval = mCurrentSlicedBucket[eventKey];
+ mCurrentSlicedBucket[eventKey].raw.push_back(make_pair(value, 0));
+}
+void ValueMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event, bool scheduledPull) {
+ uint64_t eventTimeNs = event.GetTimestampNs();
long value = get_value(event);
-
+ if (hitGuardRail(eventKey)) {
+ return;
+ }
if (mPullTagId != -1) {
- if (scheduledPull) {
- // scheduled pull always sets beginning of current bucket and end
- // of next bucket
- if (interval.raw.size() > 0) {
- interval.raw.back().second = value;
- } else {
- interval.raw.push_back(make_pair(value, value));
- }
- Interval& nextInterval = mNextSlicedBucket[eventKey];
- if (nextInterval.raw.size() == 0) {
- nextInterval.raw.push_back(make_pair(value, 0));
- } else {
- nextInterval.raw.front().first = value;
- }
- } else {
- if (mCondition == true) {
- interval.raw.push_back(make_pair(value, 0));
- } else {
- if (interval.raw.size() != 0) {
- interval.raw.back().second = value;
- } else {
- interval.tainted = true;
- VLOG("Data on condition true missing!");
- }
- }
- }
+ onMatchedLogEventInternal_pull(eventTimeNs, eventKey, value, scheduledPull);
} else {
flushIfNeeded(eventTimeNs);
- interval.raw.push_back(make_pair(value, 0));
+ onMatchedLogEventInternal_push(eventTimeNs, eventKey, value);
}
}
-long ValueMetricProducer::get_value(const LogEvent& event) {
+long ValueMetricProducer::get_value(const LogEvent& event) const {
status_t err = NO_ERROR;
long val = event.GetLong(mMetric.value_field(), &err);
if (err == NO_ERROR) {
@@ -325,6 +359,7 @@ long ValueMetricProducer::get_value(const LogEvent& event) {
}
void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+ std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex);
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
(long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
@@ -373,6 +408,7 @@ void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
}
size_t ValueMetricProducer::byteSize() const {
+ std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex);
size_t totalSize = 0;
for (const auto& pair : mPastBuckets) {
totalSize += pair.second.size() * kBucketSize;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index a024bd804d83..e87e9dabcec2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -72,6 +72,16 @@ protected:
void startNewProtoOutputStream(long long timestamp) override;
private:
+ void onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs,
+ const HashableDimensionKey& eventKey, const long& value,
+ bool scheduledPull);
+ void onMatchedLogEventInternal_push(const uint64_t& eventTimeNs,
+ const HashableDimensionKey& eventKey, const long& value);
+
+ void SerializeBuckets();
+
+ bool IsConditionMet() const;
+
const ValueMetric mMetric;
std::shared_ptr<StatsPullerManager> mStatsPullerManager;
@@ -82,8 +92,6 @@ private:
const int pullTagId, const uint64_t startTimeNs,
std::shared_ptr<StatsPullerManager> statsPullerManager);
- Mutex mLock;
-
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
@@ -102,7 +110,7 @@ private:
// TODO: Add a lock to mPastBuckets.
std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
- long get_value(const LogEvent& event);
+ long get_value(const LogEvent& event) const;
bool hitGuardRail(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 81f8eb619d35..e7e1d43974af 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -206,9 +206,10 @@ message StatsdStatsReport {
repeated int32 broadcast_sent_time_sec = 10;
repeated int32 data_drop_time_sec = 11;
- repeated MatcherStats matcher_stats = 12;
- repeated ConditionStats condition_stats = 13;
- repeated MetricStats metric_stats = 14;
+ repeated int32 dump_report_time_sec = 12;
+ repeated MatcherStats matcher_stats = 13;
+ repeated ConditionStats condition_stats = 14;
+ repeated MetricStats metric_stats = 15;
}
repeated ConfigStats config_stats = 3;
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 286f6bd75442..b14b52cf595c 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -13,8 +13,10 @@
// limitations under the License.
#include "src/guardrail/StatsdStats.h"
+#include "statslog.h"
#include <gtest/gtest.h>
+#include <vector>
#ifdef __ANDROID__
@@ -22,24 +24,204 @@ namespace android {
namespace os {
namespace statsd {
-TEST(StatsdStatsTest, TestConfigAdd) {
- // TODO: implement
+using std::vector;
+
+TEST(StatsdStatsTest, TestValidConfigAdd) {
+ StatsdStats stats;
+ string name = "StatsdTest";
+ ConfigKey key(0, name);
+ const int metricsCount = 10;
+ const int conditionsCount = 20;
+ const int matchersCount = 30;
+ const int alertsCount = 10;
+ stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount,
+ true /*valid config*/);
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false /*reset stats*/);
+
+ StatsdStatsReport report;
+ bool good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+ EXPECT_EQ(1, report.config_stats_size());
+ const auto& configReport = report.config_stats(0);
+ EXPECT_EQ(0, configReport.uid());
+ EXPECT_EQ(name, configReport.name());
+ EXPECT_EQ(metricsCount, configReport.metric_count());
+ EXPECT_EQ(conditionsCount, configReport.condition_count());
+ EXPECT_EQ(matchersCount, configReport.matcher_count());
+ EXPECT_EQ(alertsCount, configReport.alert_count());
+ EXPECT_EQ(true, configReport.is_valid());
+ EXPECT_FALSE(configReport.has_deletion_time_sec());
}
-TEST(StatsdStatsTest, TestConfigRemove) {
- // TODO: implement
+TEST(StatsdStatsTest, TestInvalidConfigAdd) {
+ StatsdStats stats;
+ string name = "StatsdTest";
+ ConfigKey key(0, name);
+ const int metricsCount = 10;
+ const int conditionsCount = 20;
+ const int matchersCount = 30;
+ const int alertsCount = 10;
+ stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount,
+ false /*bad config*/);
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false);
+
+ StatsdStatsReport report;
+ bool good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+ EXPECT_EQ(1, report.config_stats_size());
+ const auto& configReport = report.config_stats(0);
+ // The invalid config should be put into icebox with a deletion time.
+ EXPECT_TRUE(configReport.has_deletion_time_sec());
}
-TEST(StatsdStatsTest, TestMatcherReport) {
- // TODO: implement
+TEST(StatsdStatsTest, TestConfigRemove) {
+ StatsdStats stats;
+ string name = "StatsdTest";
+ ConfigKey key(0, name);
+ const int metricsCount = 10;
+ const int conditionsCount = 20;
+ const int matchersCount = 30;
+ const int alertsCount = 10;
+ stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, true);
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false);
+ StatsdStatsReport report;
+ bool good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+ EXPECT_EQ(1, report.config_stats_size());
+ const auto& configReport = report.config_stats(0);
+ EXPECT_FALSE(configReport.has_deletion_time_sec());
+
+ stats.noteConfigRemoved(key);
+ stats.dumpStats(&output, false);
+ good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+ EXPECT_EQ(1, report.config_stats_size());
+ const auto& configReport2 = report.config_stats(0);
+ EXPECT_TRUE(configReport2.has_deletion_time_sec());
}
-TEST(StatsdStatsTest, TestConditionReport) {
- // TODO: implement
+TEST(StatsdStatsTest, TestSubStats) {
+ StatsdStats stats;
+ ConfigKey key(0, "test");
+ stats.noteConfigReceived(key, 2, 3, 4, 5, true);
+
+ stats.noteMatcherMatched(key, "matcher1");
+ stats.noteMatcherMatched(key, "matcher1");
+ stats.noteMatcherMatched(key, "matcher2");
+
+ stats.noteConditionDimensionSize(key, "condition1", 250);
+ stats.noteConditionDimensionSize(key, "condition1", 240);
+
+ stats.noteMetricDimensionSize(key, "metric1", 201);
+ stats.noteMetricDimensionSize(key, "metric1", 202);
+
+ // broadcast-> 2
+ stats.noteBroadcastSent(key);
+ stats.noteBroadcastSent(key);
+
+ // data drop -> 1
+ stats.noteDataDropped(key);
+
+ // dump report -> 3
+ stats.noteMetricsReportSent(key);
+ stats.noteMetricsReportSent(key);
+ stats.noteMetricsReportSent(key);
+
+ vector<uint8_t> output;
+ stats.dumpStats(&output, true); // Dump and reset stats
+ StatsdStatsReport report;
+ bool good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+ EXPECT_EQ(1, report.config_stats_size());
+ const auto& configReport = report.config_stats(0);
+ EXPECT_EQ(2, configReport.broadcast_sent_time_sec_size());
+ EXPECT_EQ(1, configReport.data_drop_time_sec_size());
+ EXPECT_EQ(3, configReport.dump_report_time_sec_size());
+
+ EXPECT_EQ(2, configReport.matcher_stats_size());
+
+ // matcher1 is the first in the list
+ if (!configReport.matcher_stats(0).name().compare("matcher1")) {
+ EXPECT_EQ(2, configReport.matcher_stats(0).matched_times());
+ EXPECT_EQ(1, configReport.matcher_stats(1).matched_times());
+ EXPECT_EQ("matcher2", configReport.matcher_stats(1).name());
+ } else {
+ // matcher1 is the second in the list.
+ EXPECT_EQ(1, configReport.matcher_stats(0).matched_times());
+ EXPECT_EQ("matcher2", configReport.matcher_stats(0).name());
+
+ EXPECT_EQ(2, configReport.matcher_stats(1).matched_times());
+ EXPECT_EQ("matcher1", configReport.matcher_stats(1).name());
+ }
+
+ EXPECT_EQ(1, configReport.condition_stats_size());
+ EXPECT_EQ("condition1", configReport.condition_stats(0).name());
+ EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts());
+
+ EXPECT_EQ(1, configReport.metric_stats_size());
+ EXPECT_EQ("metric1", configReport.metric_stats(0).name());
+ EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts());
+
+ // after resetting the stats, some new events come
+ stats.noteMatcherMatched(key, "matcher99");
+ stats.noteConditionDimensionSize(key, "condition99", 300);
+ stats.noteMetricDimensionSize(key, "metric99", 270);
+
+ // now the config stats should only contain the stats about the new event.
+ stats.dumpStats(&output, false);
+ good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+ EXPECT_EQ(1, report.config_stats_size());
+ const auto& configReport2 = report.config_stats(0);
+ EXPECT_EQ(1, configReport2.matcher_stats_size());
+ EXPECT_EQ("matcher99", configReport2.matcher_stats(0).name());
+ EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times());
+
+ EXPECT_EQ(1, configReport2.condition_stats_size());
+ EXPECT_EQ("condition99", configReport2.condition_stats(0).name());
+ EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts());
+
+ EXPECT_EQ(1, configReport2.metric_stats_size());
+ EXPECT_EQ("metric99", configReport2.metric_stats(0).name());
+ EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts());
}
TEST(StatsdStatsTest, TestAtomLog) {
- // TODO: implement
+ StatsdStats stats;
+ time_t now = time(nullptr);
+ // old event, we get it from the stats buffer. should be ignored.
+ stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, 1000);
+
+ stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1);
+ stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
+ stats.noteAtomLogged(android::util::DROPBOX_ERROR_CHANGED, now + 3);
+ // pulled event, should ignore
+ stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFERRED, now + 4);
+
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false);
+ StatsdStatsReport report;
+ bool good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+
+ EXPECT_EQ(2, report.atom_stats_size());
+ bool sensorAtomGood = false;
+ bool dropboxAtomGood = false;
+
+ for (const auto& atomStats : report.atom_stats()) {
+ if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 2) {
+ sensorAtomGood = true;
+ }
+ if (atomStats.tag() == android::util::DROPBOX_ERROR_CHANGED && atomStats.count() == 1) {
+ dropboxAtomGood = true;
+ }
+ }
+
+ EXPECT_TRUE(dropboxAtomGood);
+ EXPECT_TRUE(sensorAtomGood);
}
} // namespace statsd
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0266131a6c0d..d7efa91fb28b 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -294,4 +294,9 @@ public abstract class ActivityManagerInternal {
* @param userId The user it is allowed for.
*/
public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId);
+
+ /**
+ * @return true if runtime was restarted, false if it's normal boot
+ */
+ public abstract boolean isRuntimeRestarted();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 95c5fb53a014..ffd012d9502f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -23,6 +23,8 @@ import android.annotation.Nullable;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
@@ -174,7 +176,7 @@ final class RemoteServiceException extends AndroidRuntimeException {
*
* {@hide}
*/
-public final class ActivityThread {
+public final class ActivityThread extends ClientTransactionHandler {
/** @hide */
public static final String TAG = "ActivityThread";
private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
@@ -401,8 +403,8 @@ public final class ActivityThread {
throw new IllegalStateException(
"Received config update for non-existing activity");
}
- activity.mMainThread.handleActivityConfigurationChanged(
- new ActivityConfigChangeData(token, overrideConfig), newDisplayId);
+ activity.mMainThread.handleActivityConfigurationChanged(token, overrideConfig,
+ newDisplayId);
};
}
@@ -469,16 +471,6 @@ public final class ActivityThread {
}
}
- static final class NewIntentData {
- List<ReferrerIntent> intents;
- IBinder token;
- boolean andPause;
- public String toString() {
- return "NewIntentData{intents=" + intents + " token=" + token
- + " andPause=" + andPause +"}";
- }
- }
-
static final class ReceiverData extends BroadcastReceiver.PendingResult {
public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
boolean ordered, boolean sticky, IBinder token, int sendingUser) {
@@ -644,14 +636,6 @@ public final class ActivityThread {
String[] args;
}
- static final class ResultData {
- IBinder token;
- List<ResultInfo> results;
- public String toString() {
- return "ResultData{token=" + token + " results" + results + "}";
- }
- }
-
static final class ContextCleanupInfo {
ContextImpl context;
String what;
@@ -679,15 +663,6 @@ public final class ActivityThread {
int flags;
}
- static final class ActivityConfigChangeData {
- final IBinder activityToken;
- final Configuration overrideConfig;
- public ActivityConfigChangeData(IBinder token, Configuration config) {
- activityToken = token;
- overrideConfig = config;
- }
- }
-
private class ApplicationThread extends IApplicationThread.Stub {
private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s";
@@ -702,93 +677,10 @@ public final class ActivityThread {
}
}
- public final void schedulePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges, boolean dontReport) {
- int seq = getLifecycleSeq();
- if (DEBUG_ORDER) Slog.d(TAG, "pauseActivity " + ActivityThread.this
- + " operation received seq: " + seq);
- sendMessage(
- finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
- token,
- (userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),
- configChanges,
- seq);
- }
-
- public final void scheduleStopActivity(IBinder token, boolean showWindow,
- int configChanges) {
- int seq = getLifecycleSeq();
- if (DEBUG_ORDER) Slog.d(TAG, "stopActivity " + ActivityThread.this
- + " operation received seq: " + seq);
- sendMessage(
- showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
- token, 0, configChanges, seq);
- }
-
- public final void scheduleWindowVisibility(IBinder token, boolean showWindow) {
- sendMessage(
- showWindow ? H.SHOW_WINDOW : H.HIDE_WINDOW,
- token);
- }
-
public final void scheduleSleeping(IBinder token, boolean sleeping) {
sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
- public final void scheduleResumeActivity(IBinder token, int processState,
- boolean isForward, Bundle resumeArgs) {
- int seq = getLifecycleSeq();
- if (DEBUG_ORDER) Slog.d(TAG, "resumeActivity " + ActivityThread.this
- + " operation received seq: " + seq);
- updateProcessState(processState, false);
- sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0, 0, seq);
- }
-
- public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
- ResultData res = new ResultData();
- res.token = token;
- res.results = results;
- sendMessage(H.SEND_RESULT, res);
- }
-
- // we use token to identify this activity without having to send the
- // activity itself back to the activity manager. (matters more with ipc)
- @Override
- public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
- CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
- int procState, Bundle state, PersistableBundle persistentState,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
-
- updateProcessState(procState, false);
-
- ActivityClientRecord r = new ActivityClientRecord();
-
- r.token = token;
- r.ident = ident;
- r.intent = intent;
- r.referrer = referrer;
- r.voiceInteractor = voiceInteractor;
- r.activityInfo = info;
- r.compatInfo = compatInfo;
- r.state = state;
- r.persistentState = persistentState;
-
- r.pendingResults = pendingResults;
- r.pendingIntents = pendingNewIntents;
-
- r.startsNotResumed = notResumed;
- r.isForward = isForward;
-
- r.profilerInfo = profilerInfo;
-
- r.overrideConfig = overrideConfig;
- updatePendingConfiguration(curConfig);
-
- sendMessage(H.LAUNCH_ACTIVITY, r);
- }
-
@Override
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
@@ -798,22 +690,6 @@ public final class ActivityThread {
configChanges, notResumed, config, overrideConfig, true, preserveWindow);
}
- public final void scheduleNewIntent(
- List<ReferrerIntent> intents, IBinder token, boolean andPause) {
- NewIntentData data = new NewIntentData();
- data.intents = intents;
- data.token = token;
- data.andPause = andPause;
-
- sendMessage(H.NEW_INTENT, data);
- }
-
- public final void scheduleDestroyActivity(IBinder token, boolean finishing,
- int configChanges) {
- sendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,
- configChanges);
- }
-
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
@@ -949,11 +825,6 @@ public final class ActivityThread {
sendMessage(H.SUICIDE, null);
}
- public void scheduleConfigurationChanged(Configuration config) {
- updatePendingConfiguration(config);
- sendMessage(H.CONFIGURATION_CHANGED, config);
- }
-
public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
sendMessage(H.APPLICATION_INFO_CHANGED, ai);
}
@@ -1016,20 +887,6 @@ public final class ActivityThread {
}
@Override
- public void scheduleActivityConfigurationChanged(
- IBinder token, Configuration overrideConfig) {
- sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED,
- new ActivityConfigChangeData(token, overrideConfig));
- }
-
- @Override
- public void scheduleActivityMovedToDisplay(IBinder token, int displayId,
- Configuration overrideConfig) {
- sendMessage(H.ACTIVITY_MOVED_TO_DISPLAY,
- new ActivityConfigChangeData(token, overrideConfig), displayId);
- }
-
- @Override
public void profilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
sendMessage(H.PROFILER_CONTROL, profilerInfo, start ? 1 : 0, profileType);
}
@@ -1427,26 +1284,6 @@ public final class ActivityThread {
}
@Override
- public void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
- Configuration overrideConfig) throws RemoteException {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = token;
- args.arg2 = overrideConfig;
- args.argi1 = isInMultiWindowMode ? 1 : 0;
- sendMessage(H.MULTI_WINDOW_MODE_CHANGED, args);
- }
-
- @Override
- public void schedulePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
- Configuration overrideConfig) throws RemoteException {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = token;
- args.arg2 = overrideConfig;
- args.argi1 = isInPipMode ? 1 : 0;
- sendMessage(H.PICTURE_IN_PICTURE_MODE_CHANGED, args);
- }
-
- @Override
public void scheduleLocalVoiceInteractionStarted(IBinder token,
IVoiceInteractor voiceInteractor) throws RemoteException {
SomeArgs args = SomeArgs.obtain();
@@ -1459,28 +1296,33 @@ public final class ActivityThread {
public void handleTrustStorageUpdate() {
NetworkSecurityPolicy.getInstance().handleTrustStorageUpdate();
}
+
+ @Override
+ public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+ ActivityThread.this.scheduleTransaction(transaction);
+ }
+ }
+
+ @Override
+ public void updatePendingConfiguration(Configuration config) {
+ mAppThread.updatePendingConfiguration(config);
+ }
+
+ @Override
+ public void updateProcessState(int processState, boolean fromIpc) {
+ mAppThread.updateProcessState(processState, fromIpc);
}
- private int getLifecycleSeq() {
+ @Override
+ public int getLifecycleSeq() {
synchronized (mResourcesManager) {
return mLifecycleSeq++;
}
}
- private class H extends Handler {
- public static final int LAUNCH_ACTIVITY = 100;
- public static final int PAUSE_ACTIVITY = 101;
- public static final int PAUSE_ACTIVITY_FINISHING= 102;
- public static final int STOP_ACTIVITY_SHOW = 103;
- public static final int STOP_ACTIVITY_HIDE = 104;
- public static final int SHOW_WINDOW = 105;
- public static final int HIDE_WINDOW = 106;
- public static final int RESUME_ACTIVITY = 107;
- public static final int SEND_RESULT = 108;
- public static final int DESTROY_ACTIVITY = 109;
+ class H extends Handler {
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
- public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
@@ -1493,7 +1335,6 @@ public final class ActivityThread {
public static final int UNBIND_SERVICE = 122;
public static final int DUMP_SERVICE = 123;
public static final int LOW_MEMORY = 124;
- public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
@@ -1518,30 +1359,17 @@ public final class ActivityThread {
public static final int ENTER_ANIMATION_COMPLETE = 149;
public static final int START_BINDER_TRACKING = 150;
public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
- public static final int MULTI_WINDOW_MODE_CHANGED = 152;
- public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
public static final int ATTACH_AGENT = 155;
public static final int APPLICATION_INFO_CHANGED = 156;
- public static final int ACTIVITY_MOVED_TO_DISPLAY = 157;
public static final int RUN_ISOLATED_ENTRY_POINT = 158;
+ public static final int EXECUTE_TRANSACTION = 159;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
- case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
- case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
- case PAUSE_ACTIVITY_FINISHING: return "PAUSE_ACTIVITY_FINISHING";
- case STOP_ACTIVITY_SHOW: return "STOP_ACTIVITY_SHOW";
- case STOP_ACTIVITY_HIDE: return "STOP_ACTIVITY_HIDE";
- case SHOW_WINDOW: return "SHOW_WINDOW";
- case HIDE_WINDOW: return "HIDE_WINDOW";
- case RESUME_ACTIVITY: return "RESUME_ACTIVITY";
- case SEND_RESULT: return "SEND_RESULT";
- case DESTROY_ACTIVITY: return "DESTROY_ACTIVITY";
case BIND_APPLICATION: return "BIND_APPLICATION";
case EXIT_APPLICATION: return "EXIT_APPLICATION";
- case NEW_INTENT: return "NEW_INTENT";
case RECEIVER: return "RECEIVER";
case CREATE_SERVICE: return "CREATE_SERVICE";
case SERVICE_ARGS: return "SERVICE_ARGS";
@@ -1553,8 +1381,6 @@ public final class ActivityThread {
case UNBIND_SERVICE: return "UNBIND_SERVICE";
case DUMP_SERVICE: return "DUMP_SERVICE";
case LOW_MEMORY: return "LOW_MEMORY";
- case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
- case ACTIVITY_MOVED_TO_DISPLAY: return "ACTIVITY_MOVED_TO_DISPLAY";
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
case PROFILER_CONTROL: return "PROFILER_CONTROL";
case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
@@ -1577,12 +1403,11 @@ public final class ActivityThread {
case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
- case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
- case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
case ATTACH_AGENT: return "ATTACH_AGENT";
case APPLICATION_INFO_CHANGED: return "APPLICATION_INFO_CHANGED";
case RUN_ISOLATED_ENTRY_POINT: return "RUN_ISOLATED_ENTRY_POINT";
+ case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
}
}
return Integer.toString(code);
@@ -1590,76 +1415,12 @@ public final class ActivityThread {
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
- case LAUNCH_ACTIVITY: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
- final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
-
- r.packageInfo = getPackageInfoNoCheck(
- r.activityInfo.applicationInfo, r.compatInfo);
- handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
- case PAUSE_ACTIVITY: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- SomeArgs args = (SomeArgs) msg.obj;
- handlePauseActivity((IBinder) args.arg1, false,
- (args.argi1 & USER_LEAVING) != 0, args.argi2,
- (args.argi1 & DONT_REPORT) != 0, args.argi3);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
- case PAUSE_ACTIVITY_FINISHING: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- SomeArgs args = (SomeArgs) msg.obj;
- handlePauseActivity((IBinder) args.arg1, true, (args.argi1 & USER_LEAVING) != 0,
- args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
- case STOP_ACTIVITY_SHOW: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
- SomeArgs args = (SomeArgs) msg.obj;
- handleStopActivity((IBinder) args.arg1, true, args.argi2, args.argi3);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
- case STOP_ACTIVITY_HIDE: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
- SomeArgs args = (SomeArgs) msg.obj;
- handleStopActivity((IBinder) args.arg1, false, args.argi2, args.argi3);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
- case SHOW_WINDOW:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
- handleWindowVisibility((IBinder)msg.obj, true);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case HIDE_WINDOW:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
- handleWindowVisibility((IBinder)msg.obj, false);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case RESUME_ACTIVITY:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
- SomeArgs args = (SomeArgs) msg.obj;
- handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
- args.argi3, "RESUME_ACTIVITY");
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case SEND_RESULT:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
- handleSendResult((ResultData)msg.obj);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case DESTROY_ACTIVITY:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
- handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
- msg.arg2, false);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
@@ -1672,11 +1433,6 @@ public final class ActivityThread {
}
Looper.myLooper().quit();
break;
- case NEW_INTENT:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
- handleNewIntent((NewIntentData)msg.obj);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
case RECEIVER:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
handleReceiver((ReceiverData)msg.obj);
@@ -1708,15 +1464,7 @@ public final class ActivityThread {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CONFIGURATION_CHANGED:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
- mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
- mUpdatingSystemConfig = true;
- try {
- handleConfigurationChanged((Configuration) msg.obj, null);
- } finally {
- mUpdatingSystemConfig = false;
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ handleConfigurationChanged((Configuration) msg.obj);
break;
case CLEAN_UP_CONTEXT:
ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
@@ -1733,18 +1481,6 @@ public final class ActivityThread {
handleLowMemory();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
- case ACTIVITY_CONFIGURATION_CHANGED:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
- handleActivityConfigurationChanged((ActivityConfigChangeData) msg.obj,
- INVALID_DISPLAY);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
- case ACTIVITY_MOVED_TO_DISPLAY:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
- handleActivityConfigurationChanged((ActivityConfigChangeData) msg.obj,
- msg.arg1 /* displayId */);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
case PROFILER_CONTROL:
handleProfilerControl(msg.arg1 != 0, (ProfilerInfo)msg.obj, msg.arg2);
break;
@@ -1828,16 +1564,6 @@ public final class ActivityThread {
case STOP_BINDER_TRACKING_AND_DUMP:
handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
break;
- case MULTI_WINDOW_MODE_CHANGED:
- handleMultiWindowModeChanged((IBinder) ((SomeArgs) msg.obj).arg1,
- ((SomeArgs) msg.obj).argi1 == 1,
- (Configuration) ((SomeArgs) msg.obj).arg2);
- break;
- case PICTURE_IN_PICTURE_MODE_CHANGED:
- handlePictureInPictureModeChanged((IBinder) ((SomeArgs) msg.obj).arg1,
- ((SomeArgs) msg.obj).argi1 == 1,
- (Configuration) ((SomeArgs) msg.obj).arg2);
- break;
case LOCAL_VOICE_INTERACTION_STARTED:
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
@@ -1857,6 +1583,9 @@ public final class ActivityThread {
handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1,
(String[]) ((SomeArgs) msg.obj).arg2);
break;
+ case EXECUTE_TRANSACTION:
+ ((ClientTransaction) msg.obj).execute(ActivityThread.this);
+ break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -2601,10 +2330,16 @@ public final class ActivityThread {
+ " req=" + requestCode + " res=" + resultCode + " data=" + data);
ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
list.add(new ResultInfo(id, requestCode, resultCode, data));
- mAppThread.scheduleSendResult(token, list);
+ final ClientTransaction clientTransaction = new ClientTransaction(mAppThread, token);
+ clientTransaction.addCallback(new ActivityResultItem(list));
+ try {
+ mAppThread.scheduleTransaction(clientTransaction);
+ } catch (RemoteException e) {
+ // Local scheduling
+ }
}
- private void sendMessage(int what, Object obj) {
+ void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
@@ -2844,6 +2579,37 @@ public final class ActivityThread {
return appContext;
}
+ @Override
+ public void handleLaunchActivity(IBinder token, Intent intent, int ident, ActivityInfo info,
+ Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer,
+ IVoiceInteractor voiceInteractor, Bundle state, PersistableBundle persistentState,
+ List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+ boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
+ ActivityClientRecord r = new ActivityClientRecord();
+
+ r.token = token;
+ r.ident = ident;
+ r.intent = intent;
+ r.referrer = referrer;
+ r.voiceInteractor = voiceInteractor;
+ r.activityInfo = info;
+ r.compatInfo = compatInfo;
+ r.state = state;
+ r.persistentState = persistentState;
+
+ r.pendingResults = pendingResults;
+ r.pendingIntents = pendingNewIntents;
+
+ r.startsNotResumed = notResumed;
+ r.isForward = isForward;
+
+ r.profilerInfo = profilerInfo;
+
+ r.overrideConfig = overrideConfig;
+ r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
+ handleLaunchActivity(r, null /* customIntent */, "LAUNCH_ACTIVITY");
+ }
+
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
@@ -2974,8 +2740,9 @@ public final class ActivityThread {
}
}
- private void handleNewIntent(NewIntentData data) {
- performNewIntents(data.token, data.intents, data.andPause);
+ @Override
+ public void handleNewIntent(IBinder token, List<ReferrerIntent> intents, boolean andPause) {
+ performNewIntents(token, intents, andPause);
}
public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
@@ -3096,7 +2863,8 @@ public final class ActivityThread {
}
}
- private void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
+ @Override
+ public void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
Configuration overrideConfig) {
final ActivityClientRecord r = mActivities.get(token);
if (r != null) {
@@ -3108,7 +2876,8 @@ public final class ActivityThread {
}
}
- private void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
+ @Override
+ public void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
Configuration overrideConfig) {
final ActivityClientRecord r = mActivities.get(token);
if (r != null) {
@@ -3619,8 +3388,9 @@ public final class ActivityThread {
r.mPendingRemoveWindowManager = null;
}
- final void handleResumeActivity(IBinder token,
- boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
+ @Override
+ public void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
+ boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
@@ -3823,7 +3593,8 @@ public final class ActivityThread {
return thumbnail;
}
- private void handlePauseActivity(IBinder token, boolean finished,
+ @Override
+ public void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport, int seq) {
ActivityClientRecord r = mActivities.get(token);
if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
@@ -4087,7 +3858,8 @@ public final class ActivityThread {
}
}
- private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
+ @Override
+ public void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "stopActivity")) {
return;
@@ -4142,7 +3914,8 @@ public final class ActivityThread {
}
}
- private void handleWindowVisibility(IBinder token, boolean show) {
+ @Override
+ public void handleWindowVisibility(IBinder token, boolean show) {
ActivityClientRecord r = mActivities.get(token);
if (r == null) {
@@ -4288,8 +4061,9 @@ public final class ActivityThread {
}
}
- private void handleSendResult(ResultData res) {
- ActivityClientRecord r = mActivities.get(res.token);
+ @Override
+ public void handleSendResult(IBinder token, List<ResultInfo> results) {
+ ActivityClientRecord r = mActivities.get(token);
if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r);
if (r != null) {
final boolean resumed = !r.paused;
@@ -4323,7 +4097,7 @@ public final class ActivityThread {
}
}
checkAndBlockForNetworkAccess();
- deliverResults(r, res.results);
+ deliverResults(r, results);
if (resumed) {
r.activity.performResume();
r.activity.mTemporaryPause = false;
@@ -4410,8 +4184,9 @@ public final class ActivityThread {
return component == null ? "[Unknown]" : component.toShortString();
}
- private void handleDestroyActivity(IBinder token, boolean finishing,
- int configChanges, boolean getNonConfigInstance) {
+ @Override
+ public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
+ boolean getNonConfigInstance) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
@@ -4982,7 +4757,20 @@ public final class ActivityThread {
return config;
}
- final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
+ @Override
+ public void handleConfigurationChanged(Configuration config) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
+ mCurDefaultDisplayDpi = config.densityDpi;
+ mUpdatingSystemConfig = true;
+ try {
+ handleConfigurationChanged(config, null /* compat */);
+ } finally {
+ mUpdatingSystemConfig = false;
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ private void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
int configDiff = 0;
@@ -5113,12 +4901,15 @@ public final class ActivityThread {
/**
* Handle new activity configuration and/or move to a different display.
- * @param data Configuration update data.
+ * @param activityToken Target activity token.
+ * @param overrideConfig Activity override config.
* @param displayId Id of the display where activity was moved to, -1 if there was no move and
* value didn't change.
*/
- void handleActivityConfigurationChanged(ActivityConfigChangeData data, int displayId) {
- ActivityClientRecord r = mActivities.get(data.activityToken);
+ @Override
+ public void handleActivityConfigurationChanged(IBinder activityToken,
+ Configuration overrideConfig, int displayId) {
+ ActivityClientRecord r = mActivities.get(activityToken);
// Check input params.
if (r == null || r.activity == null) {
if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r);
@@ -5128,14 +4919,14 @@ public final class ActivityThread {
&& displayId != r.activity.getDisplay().getDisplayId();
// Perform updates.
- r.overrideConfig = data.overrideConfig;
+ r.overrideConfig = overrideConfig;
final ViewRootImpl viewRoot = r.activity.mDecor != null
? r.activity.mDecor.getViewRootImpl() : null;
if (movedToDifferentDisplay) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:"
+ r.activityInfo.name + ", displayId=" + displayId
- + ", config=" + data.overrideConfig);
+ + ", config=" + overrideConfig);
final Configuration reportedConfig = performConfigurationChangedForActivity(r,
mCompatConfiguration, displayId, true /* movedToDifferentDisplay */);
@@ -5144,7 +4935,7 @@ public final class ActivityThread {
}
} else {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
- + r.activityInfo.name + ", config=" + data.overrideConfig);
+ + r.activityInfo.name + ", config=" + overrideConfig);
performConfigurationChangedForActivity(r, mCompatConfiguration);
}
// Notify the ViewRootImpl instance about configuration changes. It may have initiated this
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
new file mode 100644
index 000000000000..f7f4c716d0b7
--- /dev/null
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -0,0 +1,114 @@
+/*
+ * 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.app;
+
+import android.app.servertransaction.ClientTransaction;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+
+/**
+ * Defines operations that a {@link android.app.servertransaction.ClientTransaction} or its items
+ * can perform on client.
+ * @hide
+ */
+public abstract class ClientTransactionHandler {
+
+ // Schedule phase related logic and handlers.
+
+ /** Prepare and schedule transaction for execution. */
+ void scheduleTransaction(ClientTransaction transaction) {
+ transaction.prepare(this);
+ sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
+ }
+
+ abstract void sendMessage(int what, Object obj);
+
+
+ // Prepare phase related logic and handlers. Methods that inform about about pending changes or
+ // do other internal bookkeeping.
+
+ /** Get current lifecycle request number to maintain correct ordering. */
+ public abstract int getLifecycleSeq();
+
+ /** Set pending config in case it will be updated by other transaction item. */
+ public abstract void updatePendingConfiguration(Configuration config);
+
+ /** Set current process state. */
+ public abstract void updateProcessState(int processState, boolean fromIpc);
+
+
+ // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
+ // and deliver callbacks.
+
+ /** Destroy the activity. */
+ public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
+ boolean getNonConfigInstance);
+
+ /** Pause the activity. */
+ public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
+ int configChanges, boolean dontReport, int seq);
+
+ /** Resume the activity. */
+ public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
+ boolean reallyResume, int seq, String reason);
+
+ /** Stop the activity. */
+ public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
+ int seq);
+
+ /** Deliver activity (override) configuration change. */
+ public abstract void handleActivityConfigurationChanged(IBinder activityToken,
+ Configuration overrideConfig, int displayId);
+
+ /** Deliver result from another activity. */
+ public abstract void handleSendResult(IBinder token, List<ResultInfo> results);
+
+ /** Deliver multi-window mode change notification. */
+ public abstract void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
+ Configuration overrideConfig);
+
+ /** Deliver new intent. */
+ public abstract void handleNewIntent(IBinder token, List<ReferrerIntent> intents,
+ boolean andPause);
+
+ /** Deliver picture-in-picture mode change notification. */
+ public abstract void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode,
+ Configuration overrideConfig);
+
+ /** Update window visibility. */
+ public abstract void handleWindowVisibility(IBinder token, boolean show);
+
+ /** Perform activity launch. */
+ public abstract void handleLaunchActivity(IBinder token, Intent intent, int ident,
+ ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
+ String referrer, IVoiceInteractor voiceInteractor, Bundle state,
+ PersistableBundle persistentState, List<ResultInfo> pendingResults,
+ List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
+ ProfilerInfo profilerInfo);
+
+ /** Deliver app configuration change notification. */
+ public abstract void handleConfigurationChanged(Configuration config);
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5f3432264ca0..b0d020a7e328 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -59,11 +59,10 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -2457,7 +2456,8 @@ class ContextImpl extends Context {
* unable to create, they are filtered by replacing with {@code null}.
*/
private File[] ensureExternalDirsExistOrFilter(File[] dirs) {
- File[] result = new File[dirs.length];
+ final StorageManager sm = getSystemService(StorageManager.class);
+ final File[] result = new File[dirs.length];
for (int i = 0; i < dirs.length; i++) {
File dir = dirs[i];
if (!dir.exists()) {
@@ -2466,15 +2466,8 @@ class ContextImpl extends Context {
if (!dir.exists()) {
// Failing to mkdir() may be okay, since we might not have
// enough permissions; ask vold to create on our behalf.
- final IStorageManager storageManager = IStorageManager.Stub.asInterface(
- ServiceManager.getService("mount"));
try {
- final int res = storageManager.mkdirs(
- getPackageName(), dir.getAbsolutePath());
- if (res != 0) {
- Log.w(TAG, "Failed to ensure " + dir + ": " + res);
- dir = null;
- }
+ sm.mkdirs(dir);
} catch (Exception e) {
Log.w(TAG, "Failed to ensure " + dir + ": " + e);
dir = null;
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 487a94a05f7a..b25d7782652f 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -20,6 +20,7 @@ import android.app.IInstrumentationWatcher;
import android.app.IUiAutomationConnection;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
+import android.app.servertransaction.ClientTransaction;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -52,24 +53,6 @@ import java.util.Map;
* {@hide}
*/
oneway interface IApplicationThread {
- void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
- int configChanges, boolean dontReport);
- void scheduleStopActivity(IBinder token, boolean showWindow,
- int configChanges);
- void scheduleWindowVisibility(IBinder token, boolean showWindow);
- void scheduleResumeActivity(IBinder token, int procState, boolean isForward,
- in Bundle resumeArgs);
- void scheduleSendResult(IBinder token, in List<ResultInfo> results);
- void scheduleLaunchActivity(in Intent intent, IBinder token, int ident,
- in ActivityInfo info, in Configuration curConfig, in Configuration overrideConfig,
- in CompatibilityInfo compatInfo, in String referrer, IVoiceInteractor voiceInteractor,
- int procState, in Bundle state, in PersistableBundle persistentState,
- in List<ResultInfo> pendingResults, in List<ReferrerIntent> pendingNewIntents,
- boolean notResumed, boolean isForward, in ProfilerInfo profilerInfo);
- void scheduleNewIntent(
- in List<ReferrerIntent> intent, IBinder token, boolean andPause);
- void scheduleDestroyActivity(IBinder token, boolean finished,
- int configChanges);
void scheduleReceiver(in Intent intent, in ActivityInfo info,
in CompatibilityInfo compatInfo,
int resultCode, in String data, in Bundle extras, boolean sync,
@@ -87,7 +70,6 @@ oneway interface IApplicationThread {
in Bundle coreSettings, in String buildSerial);
void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
void scheduleExit();
- void scheduleConfigurationChanged(in Configuration config);
void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
void updateTimeZone();
void processInBackground();
@@ -101,9 +83,6 @@ oneway interface IApplicationThread {
int resultCode, in String data, in Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState);
void scheduleLowMemory();
- void scheduleActivityConfigurationChanged(IBinder token, in Configuration overrideConfig);
- void scheduleActivityMovedToDisplay(IBinder token, int displayId,
- in Configuration overrideConfig);
void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults,
in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
in Configuration config, in Configuration overrideConfig, boolean preserveWindow);
@@ -146,14 +125,11 @@ oneway interface IApplicationThread {
void notifyCleartextNetwork(in byte[] firstPacket);
void startBinderTracking();
void stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
- void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
- in Configuration newConfig);
- void schedulePictureInPictureModeChanged(IBinder token, boolean isInPictureInPictureMode,
- in Configuration newConfig);
void scheduleLocalVoiceInteractionStarted(IBinder token,
IVoiceInteractor voiceInteractor);
void handleTrustStorageUpdate();
void attachAgent(String path);
void scheduleApplicationInfoChanged(in ApplicationInfo ai);
void setNetworkBlockSeq(long procStateSeq);
+ void scheduleTransaction(in ClientTransaction transaction);
}
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index fad4798e3a3e..d5234278da7d 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -22,6 +22,7 @@ import android.os.Parcelable;
import android.util.Slog;
import java.io.IOException;
+import java.util.Objects;
/**
* System private API for passing profiler settings.
@@ -132,4 +133,32 @@ public class ProfilerInfo implements Parcelable {
streamingOutput = in.readInt() != 0;
agent = in.readString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ProfilerInfo other = (ProfilerInfo) o;
+ // TODO: Also check #profileFd for equality.
+ return Objects.equals(profileFile, other.profileFile)
+ && autoStopProfiler == other.autoStopProfiler
+ && samplingInterval == other.samplingInterval
+ && streamingOutput == other.streamingOutput
+ && Objects.equals(agent, other.agent);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(profileFile);
+ result = 31 * result + samplingInterval;
+ result = 31 * result + (autoStopProfiler ? 1 : 0);
+ result = 31 * result + (streamingOutput ? 1 : 0);
+ result = 31 * result + Objects.hashCode(agent);
+ return result;
+ }
}
diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java
index 5e0867c3607e..d5af08a655d8 100644
--- a/core/java/android/app/ResultInfo.java
+++ b/core/java/android/app/ResultInfo.java
@@ -20,6 +20,8 @@ import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* {@hide}
*/
@@ -79,4 +81,29 @@ public class ResultInfo implements Parcelable {
mData = null;
}
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ResultInfo)) {
+ return false;
+ }
+ final ResultInfo other = (ResultInfo) obj;
+ final boolean intentsEqual = mData == null ? (other.mData == null)
+ : mData.filterEquals(other.mData);
+ return intentsEqual && Objects.equals(mResultWho, other.mResultWho)
+ && mResultCode == other.mResultCode
+ && mRequestCode == other.mRequestCode;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + mRequestCode;
+ result = 31 * result + mResultCode;
+ result = 31 * result + Objects.hashCode(mResultWho);
+ if (mData != null) {
+ result = 31 * result + mData.filterHashCode();
+ }
+ return result;
+ }
}
diff --git a/core/java/android/app/admin/ConnectEvent.java b/core/java/android/app/admin/ConnectEvent.java
index ffd38e2b8760..f06a9257b7f8 100644
--- a/core/java/android/app/admin/ConnectEvent.java
+++ b/core/java/android/app/admin/ConnectEvent.java
@@ -32,29 +32,30 @@ import java.net.UnknownHostException;
public final class ConnectEvent extends NetworkEvent implements Parcelable {
/** The destination IP address. */
- private final String ipAddress;
+ private final String mIpAddress;
/** The destination port number. */
- private final int port;
+ private final int mPort;
/** @hide */
public ConnectEvent(String ipAddress, int port, String packageName, long timestamp) {
super(packageName, timestamp);
- this.ipAddress = ipAddress;
- this.port = port;
+ this.mIpAddress = ipAddress;
+ this.mPort = port;
}
private ConnectEvent(Parcel in) {
- this.ipAddress = in.readString();
- this.port = in.readInt();
- this.packageName = in.readString();
- this.timestamp = in.readLong();
+ this.mIpAddress = in.readString();
+ this.mPort = in.readInt();
+ this.mPackageName = in.readString();
+ this.mTimestamp = in.readLong();
+ this.mId = in.readLong();
}
public InetAddress getInetAddress() {
try {
// ipAddress is already an address, not a host name, no DNS resolution will happen.
- return InetAddress.getByName(ipAddress);
+ return InetAddress.getByName(mIpAddress);
} catch (UnknownHostException e) {
// Should never happen as we aren't passing a host name.
return InetAddress.getLoopbackAddress();
@@ -62,13 +63,13 @@ public final class ConnectEvent extends NetworkEvent implements Parcelable {
}
public int getPort() {
- return port;
+ return mPort;
}
@Override
public String toString() {
- return String.format("ConnectEvent(%s, %d, %d, %s)", ipAddress, port, timestamp,
- packageName);
+ return String.format("ConnectEvent(%s, %d, %d, %s)", mIpAddress, mPort, mTimestamp,
+ mPackageName);
}
public static final Parcelable.Creator<ConnectEvent> CREATOR
@@ -96,10 +97,10 @@ public final class ConnectEvent extends NetworkEvent implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
// write parcel token first
out.writeInt(PARCEL_TOKEN_CONNECT_EVENT);
- out.writeString(ipAddress);
- out.writeInt(port);
- out.writeString(packageName);
- out.writeLong(timestamp);
+ out.writeString(mIpAddress);
+ out.writeInt(mPort);
+ out.writeString(mPackageName);
+ out.writeLong(mTimestamp);
+ out.writeLong(mId);
}
}
-
diff --git a/core/java/android/app/admin/DnsEvent.java b/core/java/android/app/admin/DnsEvent.java
index f84c5b00a135..4ddf13e07344 100644
--- a/core/java/android/app/admin/DnsEvent.java
+++ b/core/java/android/app/admin/DnsEvent.java
@@ -34,46 +34,47 @@ import java.util.List;
public final class DnsEvent extends NetworkEvent implements Parcelable {
/** The hostname that was looked up. */
- private final String hostname;
+ private final String mHostname;
/** Contains (possibly a subset of) the IP addresses returned. */
- private final String[] ipAddresses;
+ private final String[] mIpAddresses;
/**
* The number of IP addresses returned from the DNS lookup event. May be different from the
* length of ipAddresses if there were too many addresses to log.
*/
- private final int ipAddressesCount;
+ private final int mIpAddressesCount;
/** @hide */
public DnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
String packageName, long timestamp) {
super(packageName, timestamp);
- this.hostname = hostname;
- this.ipAddresses = ipAddresses;
- this.ipAddressesCount = ipAddressesCount;
+ this.mHostname = hostname;
+ this.mIpAddresses = ipAddresses;
+ this.mIpAddressesCount = ipAddressesCount;
}
private DnsEvent(Parcel in) {
- this.hostname = in.readString();
- this.ipAddresses = in.createStringArray();
- this.ipAddressesCount = in.readInt();
- this.packageName = in.readString();
- this.timestamp = in.readLong();
+ this.mHostname = in.readString();
+ this.mIpAddresses = in.createStringArray();
+ this.mIpAddressesCount = in.readInt();
+ this.mPackageName = in.readString();
+ this.mTimestamp = in.readLong();
+ this.mId = in.readLong();
}
/** Returns the hostname that was looked up. */
public String getHostname() {
- return hostname;
+ return mHostname;
}
/** Returns (possibly a subset of) the IP addresses returned. */
public List<InetAddress> getInetAddresses() {
- if (ipAddresses == null || ipAddresses.length == 0) {
+ if (mIpAddresses == null || mIpAddresses.length == 0) {
return Collections.emptyList();
}
- final List<InetAddress> inetAddresses = new ArrayList<>(ipAddresses.length);
- for (final String ipAddress : ipAddresses) {
+ final List<InetAddress> inetAddresses = new ArrayList<>(mIpAddresses.length);
+ for (final String ipAddress : mIpAddresses) {
try {
// ipAddress is already an address, not a host name, no DNS resolution will happen.
inetAddresses.add(InetAddress.getByName(ipAddress));
@@ -90,14 +91,14 @@ public final class DnsEvent extends NetworkEvent implements Parcelable {
* addresses to log.
*/
public int getTotalResolvedAddressCount() {
- return ipAddressesCount;
+ return mIpAddressesCount;
}
@Override
public String toString() {
- return String.format("DnsEvent(%s, %s, %d, %d, %s)", hostname,
- (ipAddresses == null) ? "NONE" : String.join(" ", ipAddresses),
- ipAddressesCount, timestamp, packageName);
+ return String.format("DnsEvent(%s, %s, %d, %d, %s)", mHostname,
+ (mIpAddresses == null) ? "NONE" : String.join(" ", mIpAddresses),
+ mIpAddressesCount, mTimestamp, mPackageName);
}
public static final Parcelable.Creator<DnsEvent> CREATOR
@@ -125,11 +126,11 @@ public final class DnsEvent extends NetworkEvent implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
// write parcel token first
out.writeInt(PARCEL_TOKEN_DNS_EVENT);
- out.writeString(hostname);
- out.writeStringArray(ipAddresses);
- out.writeInt(ipAddressesCount);
- out.writeString(packageName);
- out.writeLong(timestamp);
+ out.writeString(mHostname);
+ out.writeStringArray(mIpAddresses);
+ out.writeInt(mIpAddressesCount);
+ out.writeString(mPackageName);
+ out.writeLong(mTimestamp);
+ out.writeLong(mId);
}
}
-
diff --git a/core/java/android/app/admin/NetworkEvent.java b/core/java/android/app/admin/NetworkEvent.java
index 2646c3fdba27..947e4fedbb79 100644
--- a/core/java/android/app/admin/NetworkEvent.java
+++ b/core/java/android/app/admin/NetworkEvent.java
@@ -18,8 +18,8 @@ package android.app.admin;
import android.content.pm.PackageManager;
import android.os.Parcel;
-import android.os.Parcelable;
import android.os.ParcelFormatException;
+import android.os.Parcelable;
/**
* An abstract class that represents a network event.
@@ -32,10 +32,13 @@ public abstract class NetworkEvent implements Parcelable {
static final int PARCEL_TOKEN_CONNECT_EVENT = 2;
/** The package name of the UID that performed the query. */
- String packageName;
+ String mPackageName;
/** The timestamp of the event being reported in milliseconds. */
- long timestamp;
+ long mTimestamp;
+
+ /** The id of the event. */
+ long mId;
/** @hide */
NetworkEvent() {
@@ -44,8 +47,8 @@ public abstract class NetworkEvent implements Parcelable {
/** @hide */
NetworkEvent(String packageName, long timestamp) {
- this.packageName = packageName;
- this.timestamp = timestamp;
+ this.mPackageName = packageName;
+ this.mTimestamp = timestamp;
}
/**
@@ -53,7 +56,7 @@ public abstract class NetworkEvent implements Parcelable {
* {@link PackageManager#getNameForUid}.
*/
public String getPackageName() {
- return packageName;
+ return mPackageName;
}
/**
@@ -61,7 +64,20 @@ public abstract class NetworkEvent implements Parcelable {
* the time the event was reported and midnight, January 1, 1970 UTC.
*/
public long getTimestamp() {
- return timestamp;
+ return mTimestamp;
+ }
+
+ /** @hide */
+ public void setId(long id) {
+ this.mId = id;
+ }
+
+ /**
+ * Returns the id of the event, where the id monotonically increases for each event. The id
+ * is reset when the device reboots, and when network logging is enabled.
+ */
+ public long getId() {
+ return this.mId;
}
@Override
@@ -95,4 +111,3 @@ public abstract class NetworkEvent implements Parcelable {
@Override
public abstract void writeToParcel(Parcel out, int flags);
}
-
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
new file mode 100644
index 000000000000..07001e2b0ad7
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -0,0 +1,88 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.INVALID_DISPLAY;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Activity configuration changed callback.
+ * @hide
+ */
+public class ActivityConfigurationChangeItem extends ClientTransactionItem {
+
+ private final Configuration mConfiguration;
+
+ public ActivityConfigurationChangeItem(Configuration configuration) {
+ mConfiguration = configuration;
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ // TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
+ client.handleActivityConfigurationChanged(token, mConfiguration, INVALID_DISPLAY);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedObject(mConfiguration, flags);
+ }
+
+ /** Read from Parcel. */
+ private ActivityConfigurationChangeItem(Parcel in) {
+ mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ }
+
+ public static final Creator<ActivityConfigurationChangeItem> CREATOR =
+ new Creator<ActivityConfigurationChangeItem>() {
+ public ActivityConfigurationChangeItem createFromParcel(Parcel in) {
+ return new ActivityConfigurationChangeItem(in);
+ }
+
+ public ActivityConfigurationChangeItem[] newArray(int size) {
+ return new ActivityConfigurationChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ActivityConfigurationChangeItem other = (ActivityConfigurationChangeItem) o;
+ return mConfiguration.equals(other.mConfiguration);
+ }
+
+ @Override
+ public int hashCode() {
+ return mConfiguration.hashCode();
+ }
+}
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
new file mode 100644
index 000000000000..a64108db7c21
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -0,0 +1,44 @@
+/*
+ * 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.app.servertransaction;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Request for lifecycle state that an activity should reach.
+ * @hide
+ */
+public abstract class ActivityLifecycleItem extends ClientTransactionItem {
+
+ static final boolean DEBUG_ORDER = false;
+
+ @IntDef({UNDEFINED, RESUMED, PAUSED, STOPPED, DESTROYED})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface LifecycleState{}
+ public static final int UNDEFINED = -1;
+ public static final int RESUMED = 0;
+ public static final int PAUSED = 1;
+ public static final int STOPPED = 2;
+ public static final int DESTROYED = 3;
+
+ /** A final lifecycle state that an activity should reach. */
+ @LifecycleState
+ public abstract int getTargetState();
+}
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
new file mode 100644
index 000000000000..76664d8e59e6
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -0,0 +1,95 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ResultInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Trace;
+
+import java.util.List;
+
+/**
+ * Activity result delivery callback.
+ * @hide
+ */
+public class ActivityResultItem extends ClientTransactionItem {
+
+ private final List<ResultInfo> mResultInfoList;
+
+ public ActivityResultItem(List<ResultInfo> resultInfos) {
+ mResultInfoList = resultInfos;
+ }
+
+ @Override
+ public int getPreExecutionState() {
+ return PAUSED;
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
+ client.handleSendResult(token, mResultInfoList);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(mResultInfoList, flags);
+ }
+
+ /** Read from Parcel. */
+ private ActivityResultItem(Parcel in) {
+ mResultInfoList = in.createTypedArrayList(ResultInfo.CREATOR);
+ }
+
+ public static final Parcelable.Creator<ActivityResultItem> CREATOR =
+ new Parcelable.Creator<ActivityResultItem>() {
+ public ActivityResultItem createFromParcel(Parcel in) {
+ return new ActivityResultItem(in);
+ }
+
+ public ActivityResultItem[] newArray(int size) {
+ return new ActivityResultItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ActivityResultItem other = (ActivityResultItem) o;
+ return mResultInfoList.equals(other.mResultInfoList);
+ }
+
+ @Override
+ public int hashCode() {
+ return mResultInfoList.hashCode();
+ }
+}
diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java
new file mode 100644
index 000000000000..4bd01afb5061
--- /dev/null
+++ b/core/java/android/app/servertransaction/BaseClientRequest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.app.servertransaction;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+/**
+ * Base interface for individual requests from server to client.
+ * Each of them can be prepared before scheduling and, eventually, executed.
+ * @hide
+ */
+public interface BaseClientRequest {
+
+ /**
+ * Prepare the client request before scheduling.
+ * An example of this might be informing about pending updates for some values.
+ *
+ * @param client Target client handler.
+ * @param token Target activity token.
+ */
+ default void prepare(ClientTransactionHandler client, IBinder token) {
+ }
+
+ /**
+ * Execute the request.
+ * @param client Target client handler.
+ * @param token Target activity token.
+ */
+ void execute(ClientTransactionHandler client, IBinder token);
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.aidl b/core/java/android/app/servertransaction/ClientTransaction.aidl
new file mode 100644
index 000000000000..ad8bcbf6eb04
--- /dev/null
+++ b/core/java/android/app/servertransaction/ClientTransaction.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.app.servertransaction;
+
+/** @hide */
+parcelable ClientTransaction; \ No newline at end of file
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
new file mode 100644
index 000000000000..d2289ba0d745
--- /dev/null
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -0,0 +1,201 @@
+/*
+ * 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.app.servertransaction;
+
+import android.app.ClientTransactionHandler;
+import android.app.IApplicationThread;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A container that holds a sequence of messages, which may be sent to a client.
+ * This includes a list of callbacks and a final lifecycle state.
+ *
+ * @see com.android.server.am.ClientLifecycleManager
+ * @see ClientTransactionItem
+ * @see ActivityLifecycleItem
+ * @hide
+ */
+public class ClientTransaction implements Parcelable {
+
+ /** A list of individual callbacks to a client. */
+ private List<ClientTransactionItem> mActivityCallbacks;
+
+ /**
+ * Final lifecycle state in which the client activity should be after the transaction is
+ * executed.
+ */
+ private ActivityLifecycleItem mLifecycleStateRequest;
+
+ /** Target client. */
+ private IApplicationThread mClient;
+
+ /** Target client activity. Might be null if the entire transaction is targeting an app. */
+ private IBinder mActivityToken;
+
+ public ClientTransaction(IApplicationThread client, IBinder activityToken) {
+ mClient = client;
+ mActivityToken = activityToken;
+ }
+
+ /**
+ * Add a message to the end of the sequence of callbacks.
+ * @param activityCallback A single message that can contain a lifecycle request/callback.
+ */
+ public void addCallback(ClientTransactionItem activityCallback) {
+ if (mActivityCallbacks == null) {
+ mActivityCallbacks = new ArrayList<>();
+ }
+ mActivityCallbacks.add(activityCallback);
+ }
+
+ /**
+ * Set the lifecycle state in which the client should be after executing the transaction.
+ * @param stateRequest A lifecycle request initialized with right parameters.
+ */
+ public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
+ mLifecycleStateRequest = stateRequest;
+ }
+
+ /**
+ * Do what needs to be done while the transaction is being scheduled on the client side.
+ * @param clientTransactionHandler Handler on the client side that will executed all operations
+ * requested by transaction items.
+ */
+ public void prepare(android.app.ClientTransactionHandler clientTransactionHandler) {
+ if (mActivityCallbacks != null) {
+ final int size = mActivityCallbacks.size();
+ for (int i = 0; i < size; ++i) {
+ mActivityCallbacks.get(i).prepare(clientTransactionHandler, mActivityToken);
+ }
+ }
+ if (mLifecycleStateRequest != null) {
+ mLifecycleStateRequest.prepare(clientTransactionHandler, mActivityToken);
+ }
+ }
+
+ /**
+ * Execute the transaction.
+ * @param clientTransactionHandler Handler on the client side that will execute all operations
+ * requested by transaction items.
+ */
+ public void execute(android.app.ClientTransactionHandler clientTransactionHandler) {
+ if (mActivityCallbacks != null) {
+ final int size = mActivityCallbacks.size();
+ for (int i = 0; i < size; ++i) {
+ mActivityCallbacks.get(i).execute(clientTransactionHandler, mActivityToken);
+ }
+ }
+ if (mLifecycleStateRequest != null) {
+ mLifecycleStateRequest.execute(clientTransactionHandler, mActivityToken);
+ }
+ }
+
+ /**
+ * Schedule the transaction after it was initialized. It will be send to client and all its
+ * individual parts will be applied in the following sequence:
+ * 1. The client calls {@link #prepare(ClientTransactionHandler)}, which triggers all work that
+ * needs to be done before actually scheduling the transaction for callbacks and lifecycle
+ * state request.
+ * 2. The transaction message is scheduled.
+ * 3. The client calls {@link #execute(ClientTransactionHandler)}, which executes all callbacks
+ * and necessary lifecycle transitions.
+ */
+ public void schedule() throws RemoteException {
+ mClient.scheduleTransaction(this);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mClient.asBinder());
+ final boolean writeActivityToken = mActivityToken != null;
+ dest.writeBoolean(writeActivityToken);
+ if (writeActivityToken) {
+ dest.writeStrongBinder(mActivityToken);
+ }
+ dest.writeParcelable(mLifecycleStateRequest, flags);
+ final boolean writeActivityCallbacks = mActivityCallbacks != null;
+ dest.writeBoolean(writeActivityCallbacks);
+ if (writeActivityCallbacks) {
+ dest.writeParcelableList(mActivityCallbacks, flags);
+ }
+ }
+
+ /** Read from Parcel. */
+ private ClientTransaction(Parcel in) {
+ mClient = (IApplicationThread) in.readStrongBinder();
+ final boolean readActivityToken = in.readBoolean();
+ if (readActivityToken) {
+ mActivityToken = in.readStrongBinder();
+ }
+ mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader());
+ final boolean readActivityCallbacks = in.readBoolean();
+ if (readActivityCallbacks) {
+ mActivityCallbacks = new ArrayList<>();
+ in.readParcelableList(mActivityCallbacks, getClass().getClassLoader());
+ }
+ }
+
+ public static final Creator<ClientTransaction> CREATOR =
+ new Creator<ClientTransaction>() {
+ public ClientTransaction createFromParcel(Parcel in) {
+ return new ClientTransaction(in);
+ }
+
+ public ClientTransaction[] newArray(int size) {
+ return new ClientTransaction[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ClientTransaction other = (ClientTransaction) o;
+ return Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
+ && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest)
+ && mClient == other.mClient
+ && mActivityToken == other.mActivityToken;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mActivityCallbacks);
+ result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
new file mode 100644
index 000000000000..6f2cc007ac27
--- /dev/null
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -0,0 +1,54 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import android.os.Parcelable;
+
+/**
+ * A callback message to a client that can be scheduled and executed.
+ * Examples of these might be activity configuration change, multi-window mode change, activity
+ * result delivery etc.
+ *
+ * @see ClientTransaction
+ * @see com.android.server.am.ClientLifecycleManager
+ * @hide
+ */
+public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {
+
+ /** Get the state in which this callback can be executed. */
+ @LifecycleState
+ public int getPreExecutionState() {
+ return UNDEFINED;
+ }
+
+ /** Get the state that must follow this callback. */
+ @LifecycleState
+ public int getPostExecutionState() {
+ return UNDEFINED;
+ }
+
+
+ // Parcelable
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
new file mode 100644
index 000000000000..055923ec8efd
--- /dev/null
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -0,0 +1,85 @@
+/*
+ * 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.app.servertransaction;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+/**
+ * App configuration change message.
+ * @hide
+ */
+public class ConfigurationChangeItem extends ClientTransactionItem {
+
+ private final Configuration mConfiguration;
+
+ public ConfigurationChangeItem(Configuration configuration) {
+ mConfiguration = new Configuration(configuration);
+ }
+
+ @Override
+ public void prepare(android.app.ClientTransactionHandler client, IBinder token) {
+ client.updatePendingConfiguration(mConfiguration);
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ client.handleConfigurationChanged(mConfiguration);
+ }
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedObject(mConfiguration, flags);
+ }
+
+ /** Read from Parcel. */
+ private ConfigurationChangeItem(Parcel in) {
+ mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ }
+
+ public static final Creator<ConfigurationChangeItem> CREATOR =
+ new Creator<ConfigurationChangeItem>() {
+ public ConfigurationChangeItem createFromParcel(Parcel in) {
+ return new ConfigurationChangeItem(in);
+ }
+
+ public ConfigurationChangeItem[] newArray(int size) {
+ return new ConfigurationChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ConfigurationChangeItem other = (ConfigurationChangeItem) o;
+ return mConfiguration.equals(other.mConfiguration);
+ }
+
+ @Override
+ public int hashCode() {
+ return mConfiguration.hashCode();
+ }
+}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
new file mode 100644
index 000000000000..38fd5fb6c779
--- /dev/null
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -0,0 +1,99 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Request to destroy an activity.
+ * @hide
+ */
+public class DestroyActivityItem extends ActivityLifecycleItem {
+
+ private final boolean mFinished;
+ private final int mConfigChanges;
+
+ public DestroyActivityItem(boolean finished, int configChanges) {
+ mFinished = finished;
+ mConfigChanges = configChanges;
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
+ client.handleDestroyActivity(token, mFinished, mConfigChanges,
+ false /* getNonConfigInstance */);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public int getTargetState() {
+ return DESTROYED;
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mFinished);
+ dest.writeInt(mConfigChanges);
+ }
+
+ /** Read from Parcel. */
+ private DestroyActivityItem(Parcel in) {
+ mFinished = in.readBoolean();
+ mConfigChanges = in.readInt();
+ }
+
+ public static final Creator<DestroyActivityItem> CREATOR =
+ new Creator<DestroyActivityItem>() {
+ public DestroyActivityItem createFromParcel(Parcel in) {
+ return new DestroyActivityItem(in);
+ }
+
+ public DestroyActivityItem[] newArray(int size) {
+ return new DestroyActivityItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final DestroyActivityItem other = (DestroyActivityItem) o;
+ return mFinished == other.mFinished && mConfigChanges == other.mConfigChanges;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mFinished ? 1 : 0);
+ result = 31 * result + mConfigChanges;
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
new file mode 100644
index 000000000000..417ebac8ea20
--- /dev/null
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -0,0 +1,232 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.app.ProfilerInfo;
+import android.app.ResultInfo;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.os.Trace;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Request to launch an activity.
+ * @hide
+ */
+public class LaunchActivityItem extends ActivityLifecycleItem {
+
+ private final Intent mIntent;
+ private final int mIdent;
+ private final ActivityInfo mInfo;
+ private final Configuration mCurConfig;
+ private final Configuration mOverrideConfig;
+ private final CompatibilityInfo mCompatInfo;
+ private final String mReferrer;
+ private final IVoiceInteractor mVoiceInteractor;
+ private final int mProcState;
+ private final Bundle mState;
+ private final PersistableBundle mPersistentState;
+ private final List<ResultInfo> mPendingResults;
+ private final List<ReferrerIntent> mPendingNewIntents;
+ // TODO(lifecycler): use lifecycle request instead of this param.
+ private final boolean mNotResumed;
+ private final boolean mIsForward;
+ private final ProfilerInfo mProfilerInfo;
+
+ public LaunchActivityItem(Intent intent, int ident, ActivityInfo info,
+ Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,
+ String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
+ PersistableBundle persistentState, List<ResultInfo> pendingResults,
+ List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
+ ProfilerInfo profilerInfo) {
+ mIntent = intent;
+ mIdent = ident;
+ mInfo = info;
+ mCurConfig = curConfig;
+ mOverrideConfig = overrideConfig;
+ mCompatInfo = compatInfo;
+ mReferrer = referrer;
+ mVoiceInteractor = voiceInteractor;
+ mProcState = procState;
+ mState = state;
+ mPersistentState = persistentState;
+ mPendingResults = pendingResults;
+ mPendingNewIntents = pendingNewIntents;
+ mNotResumed = notResumed;
+ mIsForward = isForward;
+ mProfilerInfo = profilerInfo;
+ }
+
+ @Override
+ public void prepare(ClientTransactionHandler client, IBinder token) {
+ client.updateProcessState(mProcState, false);
+ client.updatePendingConfiguration(mCurConfig);
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
+ client.handleLaunchActivity(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo,
+ mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults,
+ mPendingNewIntents, mNotResumed, mIsForward, mProfilerInfo);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public int getTargetState() {
+ return mNotResumed ? PAUSED : RESUMED;
+ }
+
+
+ // Parcelable implementation
+
+ /** Write from Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedObject(mIntent, flags);
+ dest.writeInt(mIdent);
+ dest.writeTypedObject(mInfo, flags);
+ dest.writeTypedObject(mCurConfig, flags);
+ dest.writeTypedObject(mOverrideConfig, flags);
+ dest.writeTypedObject(mCompatInfo, flags);
+ dest.writeString(mReferrer);
+ dest.writeStrongBinder(mVoiceInteractor != null ? mVoiceInteractor.asBinder() : null);
+ dest.writeInt(mProcState);
+ dest.writeBundle(mState);
+ dest.writePersistableBundle(mPersistentState);
+ dest.writeTypedList(mPendingResults, flags);
+ dest.writeTypedList(mPendingNewIntents, flags);
+ dest.writeBoolean(mNotResumed);
+ dest.writeBoolean(mIsForward);
+ dest.writeTypedObject(mProfilerInfo, flags);
+ }
+
+ /** Read from Parcel. */
+ private LaunchActivityItem(Parcel in) {
+ mIntent = in.readTypedObject(Intent.CREATOR);
+ mIdent = in.readInt();
+ mInfo = in.readTypedObject(ActivityInfo.CREATOR);
+ mCurConfig = in.readTypedObject(Configuration.CREATOR);
+ mOverrideConfig = in.readTypedObject(Configuration.CREATOR);
+ mCompatInfo = in.readTypedObject(CompatibilityInfo.CREATOR);
+ mReferrer = in.readString();
+ mVoiceInteractor = (IVoiceInteractor) in.readStrongBinder();
+ mProcState = in.readInt();
+ mState = in.readBundle(getClass().getClassLoader());
+ mPersistentState = in.readPersistableBundle(getClass().getClassLoader());
+ mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
+ mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+ mNotResumed = in.readBoolean();
+ mIsForward = in.readBoolean();
+ mProfilerInfo = in.readTypedObject(ProfilerInfo.CREATOR);
+ }
+
+ public static final Creator<LaunchActivityItem> CREATOR =
+ new Creator<LaunchActivityItem>() {
+ public LaunchActivityItem createFromParcel(Parcel in) {
+ return new LaunchActivityItem(in);
+ }
+
+ public LaunchActivityItem[] newArray(int size) {
+ return new LaunchActivityItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final LaunchActivityItem other = (LaunchActivityItem) o;
+ return mIntent.filterEquals(other.mIntent) && mIdent == other.mIdent
+ && activityInfoEqual(other.mInfo) && Objects.equals(mCurConfig, other.mCurConfig)
+ && Objects.equals(mOverrideConfig, other.mOverrideConfig)
+ && Objects.equals(mCompatInfo, other.mCompatInfo)
+ && Objects.equals(mReferrer, other.mReferrer)
+ && mProcState == other.mProcState && areBundlesEqual(mState, other.mState)
+ && areBundlesEqual(mPersistentState, other.mPersistentState)
+ && Objects.equals(mPendingResults, other.mPendingResults)
+ && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
+ && mNotResumed == other.mNotResumed && mIsForward == other.mIsForward
+ && Objects.equals(mProfilerInfo, other.mProfilerInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + mIntent.filterHashCode();
+ result = 31 * result + mIdent;
+ result = 31 * result + Objects.hashCode(mCurConfig);
+ result = 31 * result + Objects.hashCode(mOverrideConfig);
+ result = 31 * result + Objects.hashCode(mCompatInfo);
+ result = 31 * result + Objects.hashCode(mReferrer);
+ result = 31 * result + Objects.hashCode(mProcState);
+ result = 31 * result + (mState != null ? mState.size() : 0);
+ result = 31 * result + (mPersistentState != null ? mPersistentState.size() : 0);
+ result = 31 * result + Objects.hashCode(mPendingResults);
+ result = 31 * result + Objects.hashCode(mPendingNewIntents);
+ result = 31 * result + (mNotResumed ? 1 : 0);
+ result = 31 * result + (mIsForward ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mProfilerInfo);
+ return result;
+ }
+
+ private boolean activityInfoEqual(ActivityInfo other) {
+ return mInfo.flags == other.flags && mInfo.maxAspectRatio == other.maxAspectRatio
+ && Objects.equals(mInfo.launchToken, other.launchToken)
+ && Objects.equals(mInfo.getComponentName(), other.getComponentName());
+ }
+
+ private static boolean areBundlesEqual(BaseBundle extras, BaseBundle newExtras) {
+ if (extras == null || newExtras == null) {
+ return extras == newExtras;
+ }
+
+ if (extras.size() != newExtras.size()) {
+ return false;
+ }
+
+ for (String key : extras.keySet()) {
+ if (key != null) {
+ final Object value = extras.get(key);
+ final Object newValue = newExtras.get(key);
+ if (!Objects.equals(value, newValue)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
new file mode 100644
index 000000000000..ccd80d88620c
--- /dev/null
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -0,0 +1,93 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Activity move to a different display message.
+ * @hide
+ */
+public class MoveToDisplayItem extends ClientTransactionItem {
+
+ private final int mTargetDisplayId;
+ private final Configuration mConfiguration;
+
+ public MoveToDisplayItem(int targetDisplayId, Configuration configuration) {
+ mTargetDisplayId = targetDisplayId;
+ mConfiguration = configuration;
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
+ client.handleActivityConfigurationChanged(token, mConfiguration, mTargetDisplayId);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mTargetDisplayId);
+ dest.writeTypedObject(mConfiguration, flags);
+ }
+
+ /** Read from Parcel. */
+ private MoveToDisplayItem(Parcel in) {
+ mTargetDisplayId = in.readInt();
+ mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ }
+
+ public static final Creator<MoveToDisplayItem> CREATOR = new Creator<MoveToDisplayItem>() {
+ public MoveToDisplayItem createFromParcel(Parcel in) {
+ return new MoveToDisplayItem(in);
+ }
+
+ public MoveToDisplayItem[] newArray(int size) {
+ return new MoveToDisplayItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final MoveToDisplayItem other = (MoveToDisplayItem) o;
+ return mTargetDisplayId == other.mTargetDisplayId
+ && mConfiguration.equals(other.mConfiguration);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + mTargetDisplayId;
+ result = 31 * result + mConfiguration.hashCode();
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
new file mode 100644
index 000000000000..a0c617fa66c3
--- /dev/null
+++ b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
@@ -0,0 +1,92 @@
+/*
+ * 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.app.servertransaction;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+/**
+ * Multi-window mode change message.
+ * @hide
+ */
+// TODO(lifecycler): Remove the use of this and just use the configuration change message to
+// communicate multi-window mode change with WindowConfiguration.
+public class MultiWindowModeChangeItem extends ClientTransactionItem {
+
+ private final boolean mIsInMultiWindowMode;
+ private final Configuration mOverrideConfig;
+
+ public MultiWindowModeChangeItem(boolean isInMultiWindowMode,
+ Configuration overrideConfig) {
+ mIsInMultiWindowMode = isInMultiWindowMode;
+ mOverrideConfig = overrideConfig;
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ client.handleMultiWindowModeChanged(token, mIsInMultiWindowMode, mOverrideConfig);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mIsInMultiWindowMode);
+ dest.writeTypedObject(mOverrideConfig, flags);
+ }
+
+ /** Read from Parcel. */
+ private MultiWindowModeChangeItem(Parcel in) {
+ mIsInMultiWindowMode = in.readBoolean();
+ mOverrideConfig = in.readTypedObject(Configuration.CREATOR);
+ }
+
+ public static final Creator<MultiWindowModeChangeItem> CREATOR =
+ new Creator<MultiWindowModeChangeItem>() {
+ public MultiWindowModeChangeItem createFromParcel(Parcel in) {
+ return new MultiWindowModeChangeItem(in);
+ }
+
+ public MultiWindowModeChangeItem[] newArray(int size) {
+ return new MultiWindowModeChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final MultiWindowModeChangeItem other = (MultiWindowModeChangeItem) o;
+ return mIsInMultiWindowMode == other.mIsInMultiWindowMode
+ && mOverrideConfig.equals(other.mOverrideConfig);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mIsInMultiWindowMode ? 1 : 0);
+ result = 31 * result + mOverrideConfig.hashCode();
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
new file mode 100644
index 000000000000..61a8965aae7f
--- /dev/null
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -0,0 +1,108 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
+import static android.app.servertransaction.ActivityLifecycleItem.RESUMED;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Trace;
+
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+
+/**
+ * New intent message.
+ * @hide
+ */
+public class NewIntentItem extends ClientTransactionItem {
+
+ private final List<ReferrerIntent> mIntents;
+ private final boolean mPause;
+
+ public NewIntentItem(List<ReferrerIntent> intents, boolean pause) {
+ mIntents = intents;
+ mPause = pause;
+ }
+
+ @Override
+ public int getPreExecutionState() {
+ return PAUSED;
+ }
+
+ @Override
+ public int getPostExecutionState() {
+ return RESUMED;
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
+ client.handleNewIntent(token, mIntents, mPause);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mPause);
+ dest.writeTypedList(mIntents, flags);
+ }
+
+ /** Read from Parcel. */
+ private NewIntentItem(Parcel in) {
+ mPause = in.readBoolean();
+ mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+ }
+
+ public static final Parcelable.Creator<NewIntentItem> CREATOR =
+ new Parcelable.Creator<NewIntentItem>() {
+ public NewIntentItem createFromParcel(Parcel in) {
+ return new NewIntentItem(in);
+ }
+
+ public NewIntentItem[] newArray(int size) {
+ return new NewIntentItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final NewIntentItem other = (NewIntentItem) o;
+ return mPause == other.mPause && mIntents.equals(other.mIntents);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mPause ? 1 : 0);
+ result = 31 * result + mIntents.hashCode();
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
new file mode 100644
index 000000000000..e561a4b51427
--- /dev/null
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -0,0 +1,125 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * Request to move an activity to paused state.
+ * @hide
+ */
+public class PauseActivityItem extends ActivityLifecycleItem {
+
+ private static final String TAG = "PauseActivityItem";
+
+ private final boolean mFinished;
+ private final boolean mUserLeaving;
+ private final int mConfigChanges;
+ private final boolean mDontReport;
+
+ private int mLifecycleSeq;
+
+ public PauseActivityItem(boolean finished, boolean userLeaving, int configChanges,
+ boolean dontReport) {
+ mFinished = finished;
+ mUserLeaving = userLeaving;
+ mConfigChanges = configChanges;
+ mDontReport = dontReport;
+ }
+
+ @Override
+ public void prepare(ClientTransactionHandler client, IBinder token) {
+ mLifecycleSeq = client.getLifecycleSeq();
+ if (DEBUG_ORDER) {
+ Slog.d(TAG, "Pause transaction for " + client + " received seq: "
+ + mLifecycleSeq);
+ }
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
+ client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport,
+ mLifecycleSeq);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public int getTargetState() {
+ return PAUSED;
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mFinished);
+ dest.writeBoolean(mUserLeaving);
+ dest.writeInt(mConfigChanges);
+ dest.writeBoolean(mDontReport);
+ }
+
+ /** Read from Parcel. */
+ private PauseActivityItem(Parcel in) {
+ mFinished = in.readBoolean();
+ mUserLeaving = in.readBoolean();
+ mConfigChanges = in.readInt();
+ mDontReport = in.readBoolean();
+ }
+
+ public static final Creator<PauseActivityItem> CREATOR =
+ new Creator<PauseActivityItem>() {
+ public PauseActivityItem createFromParcel(Parcel in) {
+ return new PauseActivityItem(in);
+ }
+
+ public PauseActivityItem[] newArray(int size) {
+ return new PauseActivityItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final PauseActivityItem other = (PauseActivityItem) o;
+ return mFinished == other.mFinished && mUserLeaving == other.mUserLeaving
+ && mConfigChanges == other.mConfigChanges && mDontReport == other.mDontReport;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mFinished ? 1 : 0);
+ result = 31 * result + (mUserLeaving ? 1 : 0);
+ result = 31 * result + mConfigChanges;
+ result = 31 * result + (mDontReport ? 1 : 0);
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/PipModeChangeItem.java b/core/java/android/app/servertransaction/PipModeChangeItem.java
new file mode 100644
index 000000000000..923839eec15b
--- /dev/null
+++ b/core/java/android/app/servertransaction/PipModeChangeItem.java
@@ -0,0 +1,89 @@
+/*
+ * 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.app.servertransaction;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+/**
+ * Picture in picture mode change message.
+ * @hide
+ */
+// TODO(lifecycler): Remove the use of this and just use the configuration change message to
+// communicate multi-window mode change with WindowConfiguration.
+public class PipModeChangeItem extends ClientTransactionItem {
+
+ private final boolean mIsInPipMode;
+ private final Configuration mOverrideConfig;
+
+ public PipModeChangeItem(boolean isInPipMode, Configuration overrideConfig) {
+ mIsInPipMode = isInPipMode;
+ mOverrideConfig = overrideConfig;
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ client.handlePictureInPictureModeChanged(token, mIsInPipMode, mOverrideConfig);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mIsInPipMode);
+ dest.writeTypedObject(mOverrideConfig, flags);
+ }
+
+ /** Read from Parcel. */
+ private PipModeChangeItem(Parcel in) {
+ mIsInPipMode = in.readBoolean();
+ mOverrideConfig = in.readTypedObject(Configuration.CREATOR);
+ }
+
+ public static final Creator<PipModeChangeItem> CREATOR =
+ new Creator<PipModeChangeItem>() {
+ public PipModeChangeItem createFromParcel(Parcel in) {
+ return new PipModeChangeItem(in);
+ }
+
+ public PipModeChangeItem[] newArray(int size) {
+ return new PipModeChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final PipModeChangeItem other = (PipModeChangeItem) o;
+ return mIsInPipMode == other.mIsInPipMode && mOverrideConfig.equals(other.mOverrideConfig);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mIsInPipMode ? 1 : 0);
+ result = 31 * result + mOverrideConfig.hashCode();
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
new file mode 100644
index 000000000000..ea31a461f1f3
--- /dev/null
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -0,0 +1,114 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * Request to move an activity to resumed state.
+ * @hide
+ */
+public class ResumeActivityItem extends ActivityLifecycleItem {
+
+ private static final String TAG = "ResumeActivityItem";
+
+ private final int mProcState;
+ private final boolean mIsForward;
+
+ private int mLifecycleSeq;
+
+ public ResumeActivityItem(int procState, boolean isForward) {
+ mProcState = procState;
+ mIsForward = isForward;
+ }
+
+ @Override
+ public void prepare(ClientTransactionHandler client, IBinder token) {
+ mLifecycleSeq = client.getLifecycleSeq();
+ if (DEBUG_ORDER) {
+ Slog.d(TAG, "Resume transaction for " + client + " received seq: "
+ + mLifecycleSeq);
+ }
+ client.updateProcessState(mProcState, false);
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
+ client.handleResumeActivity(token, true /* clearHide */, mIsForward,
+ true /* reallyResume */, mLifecycleSeq, "RESUME_ACTIVITY");
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public int getTargetState() {
+ return RESUMED;
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mProcState);
+ dest.writeBoolean(mIsForward);
+ }
+
+ /** Read from Parcel. */
+ private ResumeActivityItem(Parcel in) {
+ mProcState = in.readInt();
+ mIsForward = in.readBoolean();
+ }
+
+ public static final Creator<ResumeActivityItem> CREATOR =
+ new Creator<ResumeActivityItem>() {
+ public ResumeActivityItem createFromParcel(Parcel in) {
+ return new ResumeActivityItem(in);
+ }
+
+ public ResumeActivityItem[] newArray(int size) {
+ return new ResumeActivityItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ResumeActivityItem other = (ResumeActivityItem) o;
+ return mProcState == other.mProcState && mIsForward == other.mIsForward;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + mProcState;
+ result = 31 * result + (mIsForward ? 1 : 0);
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
new file mode 100644
index 000000000000..d62c507770a2
--- /dev/null
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -0,0 +1,112 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * Request to move an activity to stopped state.
+ * @hide
+ */
+public class StopActivityItem extends ActivityLifecycleItem {
+
+ private static final String TAG = "StopActivityItem";
+
+ private final boolean mShowWindow;
+ private final int mConfigChanges;
+
+ private int mLifecycleSeq;
+
+ public StopActivityItem(boolean showWindow, int configChanges) {
+ mShowWindow = showWindow;
+ mConfigChanges = configChanges;
+ }
+
+ @Override
+ public void prepare(ClientTransactionHandler client, IBinder token) {
+ mLifecycleSeq = client.getLifecycleSeq();
+ if (DEBUG_ORDER) {
+ Slog.d(TAG, "Stop transaction for " + client + " received seq: "
+ + mLifecycleSeq);
+ }
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
+ client.handleStopActivity(token, mShowWindow, mConfigChanges, mLifecycleSeq);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public int getTargetState() {
+ return STOPPED;
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mShowWindow);
+ dest.writeInt(mConfigChanges);
+ }
+
+ /** Read from Parcel. */
+ private StopActivityItem(Parcel in) {
+ mShowWindow = in.readBoolean();
+ mConfigChanges = in.readInt();
+ }
+
+ public static final Creator<StopActivityItem> CREATOR =
+ new Creator<StopActivityItem>() {
+ public StopActivityItem createFromParcel(Parcel in) {
+ return new StopActivityItem(in);
+ }
+
+ public StopActivityItem[] newArray(int size) {
+ return new StopActivityItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final StopActivityItem other = (StopActivityItem) o;
+ return mShowWindow == other.mShowWindow && mConfigChanges == other.mConfigChanges;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mShowWindow ? 1 : 0);
+ result = 31 * result + mConfigChanges;
+ return result;
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java
new file mode 100644
index 000000000000..8e88b38d50b1
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowVisibilityItem.java
@@ -0,0 +1,85 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+
+/**
+ * Window visibility change message.
+ * @hide
+ */
+public class WindowVisibilityItem extends ClientTransactionItem {
+
+ private final boolean mShowWindow;
+
+ public WindowVisibilityItem(boolean showWindow) {
+ mShowWindow = showWindow;
+ }
+
+ @Override
+ public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
+ client.handleWindowVisibility(token, mShowWindow);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mShowWindow);
+ }
+
+ /** Read from Parcel. */
+ private WindowVisibilityItem(Parcel in) {
+ mShowWindow = in.readBoolean();
+ }
+
+ public static final Creator<WindowVisibilityItem> CREATOR =
+ new Creator<WindowVisibilityItem>() {
+ public WindowVisibilityItem createFromParcel(Parcel in) {
+ return new WindowVisibilityItem(in);
+ }
+
+ public WindowVisibilityItem[] newArray(int size) {
+ return new WindowVisibilityItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowVisibilityItem other = (WindowVisibilityItem) o;
+ return mShowWindow == other.mShowWindow;
+ }
+
+ @Override
+ public int hashCode() {
+ return 17 + 31 * (mShowWindow ? 1 : 0);
+ }
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 837c00a72784..f8cdce64c139 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1156,6 +1156,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
dest.writeString(permission);
dest.writeString(taskAffinity);
dest.writeString(targetActivity);
+ dest.writeString(launchToken);
dest.writeInt(flags);
dest.writeInt(screenOrientation);
dest.writeInt(configChanges);
@@ -1282,6 +1283,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
permission = source.readString();
taskAffinity = source.readString();
targetActivity = source.readString();
+ launchToken = source.readString();
flags = source.readInt();
screenOrientation = source.readInt();
configChanges = source.readInt();
diff --git a/core/java/android/content/pm/InstantAppInfo.java b/core/java/android/content/pm/InstantAppInfo.java
index 67afc928fd78..cb04fc3ce8e1 100644
--- a/core/java/android/content/pm/InstantAppInfo.java
+++ b/core/java/android/content/pm/InstantAppInfo.java
@@ -18,6 +18,7 @@ package android.content.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,6 +32,7 @@ import android.os.Parcelable;
*
* @hide
*/
+@SystemApi
public final class InstantAppInfo implements Parcelable {
private final ApplicationInfo mApplicationInfo;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 68bb5620de85..ebeaad78ea6a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1258,9 +1258,12 @@ public class PackageParser {
}
}
- pkg.setCodePath(packageDir.getAbsolutePath());
+ pkg.setCodePath(packageDir.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to get path: " + lite.baseCodePath, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
@@ -1289,9 +1292,12 @@ public class PackageParser {
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);
- pkg.setCodePath(apkFile.getAbsolutePath());
+ pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to get path: " + apkFile, e);
} finally {
IoUtils.closeQuietly(assets);
}
diff --git a/core/java/android/database/OWNERS b/core/java/android/database/OWNERS
new file mode 100644
index 000000000000..84e81e4285d1
--- /dev/null
+++ b/core/java/android/database/OWNERS
@@ -0,0 +1,2 @@
+fkupolov@google.com
+omakoto@google.com \ No newline at end of file
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index af91e81959d1..811091e37da2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -224,6 +224,7 @@ public abstract class BatteryStats implements Parcelable {
* - Always On Display (screen doze mode) time and power
* New in version 28:
* - Light/Deep Doze power
+ * - WiFi Multicast Wakelock statistics (count & duration)
*/
static final int CHECKIN_VERSION = 28;
@@ -313,6 +314,8 @@ public abstract class BatteryStats implements Parcelable {
private static final String CAMERA_DATA = "cam";
private static final String VIDEO_DATA = "vid";
private static final String AUDIO_DATA = "aud";
+ private static final String WIFI_MULTICAST_TOTAL_DATA = "wmct";
+ private static final String WIFI_MULTICAST_DATA = "wmc";
public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
@@ -516,6 +519,13 @@ public abstract class BatteryStats implements Parcelable {
public abstract ArrayMap<String, ? extends Wakelock> getWakelockStats();
/**
+ * Returns the WiFi Multicast Wakelock statistics.
+ *
+ * @return a Timer Object for the per uid Multicast statistics.
+ */
+ public abstract Timer getMulticastWakelockStats();
+
+ /**
* Returns a mapping containing sync statistics.
*
* @return a Map from Strings to Timer objects.
@@ -3363,13 +3373,16 @@ public abstract class BatteryStats implements Parcelable {
screenDozeTime / 1000);
- // Calculate wakelock times across all uids.
+ // Calculate both wakelock and wifi multicast wakelock times across all uids.
long fullWakeLockTimeTotal = 0;
long partialWakeLockTimeTotal = 0;
+ long multicastWakeLockTimeTotalMicros = 0;
+ int multicastWakeLockCountTotal = 0;
for (int iu = 0; iu < NU; iu++) {
final Uid u = uidStats.valueAt(iu);
+ // First calculating the wakelock stats
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
= u.getWakelockStats();
for (int iw=wakelocks.size()-1; iw>=0; iw--) {
@@ -3387,6 +3400,13 @@ public abstract class BatteryStats implements Parcelable {
rawRealtime, which);
}
}
+
+ // Now calculating the wifi multicast wakelock stats
+ final Timer mcTimer = u.getMulticastWakelockStats();
+ if (mcTimer != null) {
+ multicastWakeLockTimeTotalMicros += mcTimer.getTotalTimeLocked(rawRealtime, which);
+ multicastWakeLockCountTotal += mcTimer.getCountLocked(which);
+ }
}
// Dump network stats
@@ -3502,6 +3522,11 @@ public abstract class BatteryStats implements Parcelable {
}
dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_COUNT_DATA, args);
+ // Dump Multicast total stats
+ dumpLine(pw, 0 /* uid */, category, WIFI_MULTICAST_TOTAL_DATA,
+ multicastWakeLockTimeTotalMicros / 1000,
+ multicastWakeLockCountTotal);
+
if (which == STATS_SINCE_UNPLUGGED) {
dumpLine(pw, 0 /* uid */, category, BATTERY_LEVEL_DATA, getDischargeStartLevel(),
getDischargeCurrentLevel());
@@ -3828,6 +3853,18 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ // WiFi Multicast Wakelock Statistics
+ final Timer mcTimer = u.getMulticastWakelockStats();
+ if (mcTimer != null) {
+ final long totalMcWakelockTimeMs =
+ mcTimer.getTotalTimeLocked(rawRealtime, which) / 1000 ;
+ final int countMcWakelock = mcTimer.getCountLocked(which);
+ if(totalMcWakelockTimeMs > 0) {
+ dumpLine(pw, uid, category, WIFI_MULTICAST_DATA,
+ totalMcWakelockTimeMs, countMcWakelock);
+ }
+ }
+
final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
for (int isy=syncs.size()-1; isy>=0; isy--) {
final Timer timer = syncs.valueAt(isy);
@@ -4327,15 +4364,18 @@ public abstract class BatteryStats implements Parcelable {
pw.print(" Connectivity changes: "); pw.println(connChanges);
}
- // Calculate wakelock times across all uids.
+ // Calculate both wakelock and wifi multicast wakelock times across all uids.
long fullWakeLockTimeTotalMicros = 0;
long partialWakeLockTimeTotalMicros = 0;
+ long multicastWakeLockTimeTotalMicros = 0;
+ int multicastWakeLockCountTotal = 0;
final ArrayList<TimerEntry> timers = new ArrayList<>();
for (int iu = 0; iu < NU; iu++) {
final Uid u = uidStats.valueAt(iu);
+ // First calculate wakelock statistics
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
= u.getWakelockStats();
for (int iw=wakelocks.size()-1; iw>=0; iw--) {
@@ -4363,6 +4403,13 @@ public abstract class BatteryStats implements Parcelable {
}
}
}
+
+ // Next calculate wifi multicast wakelock statistics
+ final Timer mcTimer = u.getMulticastWakelockStats();
+ if (mcTimer != null) {
+ multicastWakeLockTimeTotalMicros += mcTimer.getTotalTimeLocked(rawRealtime, which);
+ multicastWakeLockCountTotal += mcTimer.getCountLocked(which);
+ }
}
final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
@@ -4392,6 +4439,20 @@ public abstract class BatteryStats implements Parcelable {
pw.println(sb.toString());
}
+ if (multicastWakeLockTimeTotalMicros != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Total WiFi Multicast wakelock Count: ");
+ sb.append(multicastWakeLockCountTotal);
+ pw.println(sb.toString());
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Total WiFi Multicast wakelock time: ");
+ formatTimeMsNoSpace(sb, (multicastWakeLockTimeTotalMicros + 500) / 1000);
+ pw.println(sb.toString());
+ }
+
pw.println("");
pw.print(prefix);
sb.setLength(0);
@@ -5309,6 +5370,24 @@ public abstract class BatteryStats implements Parcelable {
}
}
+ // Calculate multicast wakelock stats
+ final Timer mcTimer = u.getMulticastWakelockStats();
+ if (mcTimer != null) {
+ final long multicastWakeLockTimeMicros = mcTimer.getTotalTimeLocked(rawRealtime, which);
+ final int multicastWakeLockCount = mcTimer.getCountLocked(which);
+
+ if (multicastWakeLockTimeMicros > 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" WiFi Multicast Wakelock");
+ sb.append(" count = ");
+ sb.append(multicastWakeLockCount);
+ sb.append(" time = ");
+ formatTimeMsNoSpace(sb, (multicastWakeLockTimeMicros + 500) / 1000);
+ pw.println(sb.toString());
+ }
+ }
+
final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
for (int isy=syncs.size()-1; isy>=0; isy--) {
final Timer timer = syncs.valueAt(isy);
@@ -7085,6 +7164,10 @@ public abstract class BatteryStats implements Parcelable {
proto.end(wToken);
}
+ // Wifi Multicast Wakelock (WIFI_MULTICAST_WAKELOCK_DATA)
+ dumpTimer(proto, UidProto.WIFI_MULTICAST_WAKELOCK, u.getMulticastWakelockStats(),
+ rawRealtimeUs, which);
+
// Wakeup alarms (WAKEUP_ALARM_DATA)
for (int ipkg = packageStats.size() - 1; ipkg >= 0; --ipkg) {
final Uid.Pkg ps = packageStats.valueAt(ipkg);
@@ -7341,6 +7424,30 @@ public abstract class BatteryStats implements Parcelable {
getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
proto.end(mToken);
+ // Wifi multicast wakelock total stats (WIFI_MULTICAST_WAKELOCK_TOTAL_DATA)
+ // Calculate multicast wakelock stats across all uids.
+ long multicastWakeLockTimeTotalUs = 0;
+ int multicastWakeLockCountTotal = 0;
+
+ for (int iu = 0; iu < uidStats.size(); iu++) {
+ final Uid u = uidStats.valueAt(iu);
+
+ final Timer mcTimer = u.getMulticastWakelockStats();
+
+ if (mcTimer != null) {
+ multicastWakeLockTimeTotalUs +=
+ mcTimer.getTotalTimeLocked(rawRealtimeUs, which);
+ multicastWakeLockCountTotal += mcTimer.getCountLocked(which);
+ }
+ }
+
+ final long wmctToken = proto.start(SystemProto.WIFI_MULTICAST_WAKELOCK_TOTAL);
+ proto.write(SystemProto.WifiMulticastWakelockTotal.DURATION_MS,
+ multicastWakeLockTimeTotalUs / 1000);
+ proto.write(SystemProto.WifiMulticastWakelockTotal.COUNT,
+ multicastWakeLockCountTotal);
+ proto.end(wmctToken);
+
// Power use item (POWER_USE_ITEM_DATA)
final List<BatterySipper> sippers = helper.getUsageList();
if (sippers != null) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 5dd8d05cfb98..068f5f7f51cc 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -555,6 +555,11 @@ public final class PowerManager {
int FORCE_ALL_APPS_STANDBY = 11;
/**
+ * Whether to enable background check on all apps or not.
+ */
+ int FORCE_BACKGROUND_CHECK = 12;
+
+ /**
* Whether to disable non-essential sensors. (e.g. edge sensors.)
*/
int OPTIONAL_SENSORS = 13;
diff --git a/core/java/android/os/UEventObserver.java b/core/java/android/os/UEventObserver.java
index 5c80ca6559ae..69a3922585ce 100644
--- a/core/java/android/os/UEventObserver.java
+++ b/core/java/android/os/UEventObserver.java
@@ -108,7 +108,7 @@ public abstract class UEventObserver {
* UEventObserver after this call. Repeated calls have no effect.
*/
public final void stopObserving() {
- final UEventThread t = getThread();
+ final UEventThread t = peekThread();
if (t != null) {
t.removeObserver(this);
}
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 2ffc7b028d9b..3b53260e5dd7 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -112,7 +112,7 @@ interface IStorageManager {
* path belongs to a volume managed by vold, and that path is either
* external storage data or OBB directory belonging to calling app.
*/
- int mkdirs(in String callingPkg, in String path) = 34;
+ void mkdirs(in String callingPkg, in String path) = 34;
/**
* Determines the type of the encryption password
* @return PasswordType
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 0b007ddf50a1..4796712f2d98 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1123,6 +1123,15 @@ public class StorageManager {
return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace());
}
+ /** {@hide} */
+ public void mkdirs(File file) {
+ try {
+ mStorageManager.mkdirs(mContext.getOpPackageName(), file.getAbsolutePath());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @removed */
public @NonNull StorageVolume[] getVolumeList() {
return getVolumeList(mContext.getUserId(), 0);
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index ff8b9dd77068..adf437cedd90 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -62,11 +62,25 @@ public class SearchIndexablesContract {
public static final String NON_INDEXABLES_KEYS = "non_indexables_key";
/**
+ * Site map pairs data key
+ *
+ * @hide
+ */
+ public static final String SITE_MAP_PAIRS_KEYS = "site_map_pairs";
+
+ /**
* ContentProvider path for non indexable data keys.
*/
public static final String NON_INDEXABLES_KEYS_PATH = SETTINGS + "/" + NON_INDEXABLES_KEYS;
/**
+ * ContentProvider path for sitemap keys.
+ *
+ * @hide
+ */
+ public static final String SITE_MAP_PAIRS_PATH = SETTINGS + "/" + SITE_MAP_PAIRS_KEYS;
+
+ /**
* Indexable xml resources columns.
*/
public static final String[] INDEXABLES_XML_RES_COLUMNS = new String[] {
@@ -113,6 +127,18 @@ public class SearchIndexablesContract {
};
/**
+ * Columns for site map queries.
+ *
+ * @hide
+ */
+ public static final String[] SITE_MAP_COLUMNS = new String[] {
+ SiteMapColumns.PARENT_CLASS,
+ SiteMapColumns.PARENT_TITLE,
+ SiteMapColumns.CHILD_CLASS,
+ SiteMapColumns.CHILD_TITLE,
+ };
+
+ /**
* Indexable raw data columns indices.
*/
public static final int COLUMN_INDEX_RAW_RANK = 0;
@@ -169,6 +195,16 @@ public class SearchIndexablesContract {
}
/**
+ * @hide
+ */
+ public static final class SiteMapColumns {
+ public static final String PARENT_CLASS = "parent_class";
+ public static final String CHILD_CLASS = "child_class";
+ public static final String PARENT_TITLE = "parent_title";
+ public static final String CHILD_TITLE = "child_title";
+ }
+
+ /**
* Constants related to a {@link SearchIndexableData}.
*
* This is the raw data that is stored into an Index. This is related to
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 3120e543561d..138e77b69627 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -72,6 +72,7 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
private static final int MATCH_RES_CODE = 1;
private static final int MATCH_RAW_CODE = 2;
private static final int MATCH_NON_INDEXABLE_KEYS_CODE = 3;
+ private static final int MATCH_SITE_MAP_PAIRS_CODE = 4;
/**
* Implementation is provided by the parent class.
@@ -87,6 +88,8 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
MATCH_RAW_CODE);
mMatcher.addURI(mAuthority, SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH,
MATCH_NON_INDEXABLE_KEYS_CODE);
+ mMatcher.addURI(mAuthority, SearchIndexablesContract.SITE_MAP_PAIRS_PATH,
+ MATCH_SITE_MAP_PAIRS_CODE);
// Sanity check our setup
if (!info.exported) {
@@ -112,6 +115,8 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
return queryRawData(null);
case MATCH_NON_INDEXABLE_KEYS_CODE:
return queryNonIndexableKeys(null);
+ case MATCH_SITE_MAP_PAIRS_CODE:
+ return querySiteMapPairs();
default:
throw new UnsupportedOperationException("Unknown Uri " + uri);
}
@@ -150,6 +155,17 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
*/
public abstract Cursor queryNonIndexableKeys(String[] projection);
+ /**
+ * Returns a {@link Cursor}, where rows are [parent class, child class] entries to form a site
+ * map. The list of pairs should be as complete as possible.
+ *
+ * @hide
+ */
+ public Cursor querySiteMapPairs() {
+ // By default no-op.
+ return null;
+ }
+
@Override
public String getType(Uri uri) {
switch (mMatcher.match(uri)) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 994575545c5c..729c0ff6febb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9379,6 +9379,9 @@ public final class Settings {
/** {@hide} */
public static final String
BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_";
+ /** {@hide} */
+ public static final String
+ BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_";
/**
* Activity manager specific settings.
@@ -9745,6 +9748,14 @@ public final class Settings {
}
/**
+ * Get the key that retrieves a bluetooth hearing aid priority.
+ * @hide
+ */
+ public static final String getBluetoothHearingAidPriorityKey(String address) {
+ return BLUETOOTH_HEARING_AID_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+ }
+
+ /**
* Get the key that retrieves a bluetooth map priority.
* @hide
*/
diff --git a/core/java/android/service/autofill/NegationValidator.java b/core/java/android/service/autofill/NegationValidator.java
new file mode 100644
index 000000000000..a963f9f94346
--- /dev/null
+++ b/core/java/android/service/autofill/NegationValidator.java
@@ -0,0 +1,79 @@
+/*
+ * 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.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Validator used to implement a {@code NOT} logical operation.
+ *
+ * @hide
+ */
+final class NegationValidator extends InternalValidator {
+ @NonNull private final InternalValidator mValidator;
+
+ NegationValidator(@NonNull InternalValidator validator) {
+ mValidator = Preconditions.checkNotNull(validator);
+ }
+
+ @Override
+ public boolean isValid(@NonNull ValueFinder finder) {
+ return !mValidator.isValid(finder);
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ if (!sDebug) return super.toString();
+
+ return "NegationValidator: [validator=" + mValidator + "]";
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mValidator, flags);
+ }
+
+ public static final Parcelable.Creator<NegationValidator> CREATOR =
+ new Parcelable.Creator<NegationValidator>() {
+ @Override
+ public NegationValidator createFromParcel(Parcel parcel) {
+ return new NegationValidator(parcel.readParcelable(null));
+ }
+
+ @Override
+ public NegationValidator[] newArray(int size) {
+ return new NegationValidator[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/Validators.java b/core/java/android/service/autofill/Validators.java
index 51b503c21690..1c8386870fe7 100644
--- a/core/java/android/service/autofill/Validators.java
+++ b/core/java/android/service/autofill/Validators.java
@@ -52,6 +52,19 @@ public final class Validators {
return new OptionalValidators(getInternalValidators(validators));
}
+ /**
+ * Creates a validator that is valid only if {@code validator} is not.
+ *
+ * @throws IllegalArgumentException if {@code validator} is an instance of a class that is not
+ * provided by the Android System.
+ */
+ @NonNull
+ public static Validator not(@NonNull Validator validator) {
+ Preconditions.checkArgument(validator instanceof InternalValidator,
+ "validator not provided by Android System: " + validator);
+ return new NegationValidator((InternalValidator) validator);
+ }
+
private static InternalValidator[] getInternalValidators(Validator[] validators) {
Preconditions.checkArrayElementsNotNull(validators, "validators");
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 1c6275fb8dc1..dd0ae3393f1d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -889,7 +889,8 @@ public abstract class WallpaperService extends Service {
mFinalStableInsets.set(mDispatchedStableInsets);
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
null, mFinalStableInsets,
- getResources().getConfiguration().isScreenRound(), false);
+ getResources().getConfiguration().isScreenRound(), false,
+ null /* displayCutout */);
if (DEBUG) {
Log.v(TAG, "dispatching insets=" + insets);
}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
new file mode 100644
index 000000000000..19cd42e1fa18
--- /dev/null
+++ b/core/java/android/view/DisplayCutout.java
@@ -0,0 +1,408 @@
+/*
+ * 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.view;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a part of the display that is not functional for displaying content.
+ *
+ * <p>{@code DisplayCutout} is immutable.
+ *
+ * @hide will become API
+ */
+public final class DisplayCutout {
+
+ private static final Rect ZERO_RECT = new Rect(0, 0, 0, 0);
+ private static final ArrayList<Point> EMPTY_LIST = new ArrayList<>();
+
+ /**
+ * An instance where {@link #hasCutout()} returns {@code false}.
+ *
+ * @hide
+ */
+ public static final DisplayCutout NO_CUTOUT =
+ new DisplayCutout(ZERO_RECT, ZERO_RECT, EMPTY_LIST);
+
+ private final Rect mSafeInsets;
+ private final Rect mBoundingRect;
+ private final List<Point> mBoundingPolygon;
+
+ /**
+ * Creates a DisplayCutout instance.
+ *
+ * NOTE: the Rects passed into this instance are not copied and MUST remain unchanged.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public DisplayCutout(Rect safeInsets, Rect boundingRect, List<Point> boundingPolygon) {
+ mSafeInsets = safeInsets != null ? safeInsets : ZERO_RECT;
+ mBoundingRect = boundingRect != null ? boundingRect : ZERO_RECT;
+ mBoundingPolygon = boundingPolygon != null ? boundingPolygon : EMPTY_LIST;
+ }
+
+ /**
+ * Returns whether there is a cutout.
+ *
+ * If false, the safe insets will all return zero, and the bounding box or polygon will be
+ * empty or outside the content view.
+ *
+ * @return {@code true} if there is a cutout, {@code false} otherwise
+ */
+ public boolean hasCutout() {
+ return !mSafeInsets.equals(ZERO_RECT);
+ }
+
+ /** Returns the inset from the top which avoids the display cutout. */
+ public int getSafeInsetTop() {
+ return mSafeInsets.top;
+ }
+
+ /** Returns the inset from the bottom which avoids the display cutout. */
+ public int getSafeInsetBottom() {
+ return mSafeInsets.bottom;
+ }
+
+ /** Returns the inset from the left which avoids the display cutout. */
+ public int getSafeInsetLeft() {
+ return mSafeInsets.left;
+ }
+
+ /** Returns the inset from the right which avoids the display cutout. */
+ public int getSafeInsetRight() {
+ return mSafeInsets.right;
+ }
+
+ /**
+ * Obtains the safe insets in a rect.
+ *
+ * @param out a rect which is set to the safe insets.
+ * @hide
+ */
+ public void getSafeInsets(@NonNull Rect out) {
+ out.set(mSafeInsets);
+ }
+
+ /**
+ * Obtains the bounding rect of the cutout.
+ *
+ * @param outRect is filled with the bounding rect of the cutout. Coordinates are relative
+ * to the top-left corner of the content view.
+ */
+ public void getBoundingRect(@NonNull Rect outRect) {
+ outRect.set(mBoundingRect);
+ }
+
+ /**
+ * Obtains the bounding polygon of the cutout.
+ *
+ * @param outPolygon is filled with a list of points representing the corners of a convex
+ * polygon which covers the cutout. Coordinates are relative to the
+ * top-left corner of the content view.
+ */
+ public void getBoundingPolygon(List<Point> outPolygon) {
+ outPolygon.clear();
+ for (int i = 0; i < mBoundingPolygon.size(); i++) {
+ outPolygon.add(new Point(mBoundingPolygon.get(i)));
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mSafeInsets.hashCode();
+ result = result * 31 + mBoundingRect.hashCode();
+ result = result * 31 + mBoundingPolygon.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof DisplayCutout) {
+ DisplayCutout c = (DisplayCutout) o;
+ return mSafeInsets.equals(c.mSafeInsets)
+ && mBoundingRect.equals(c.mBoundingRect)
+ && mBoundingPolygon.equals(c.mBoundingPolygon);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "DisplayCutout{insets=" + mSafeInsets
+ + " bounding=" + mBoundingRect
+ + "}";
+ }
+
+ /**
+ * Insets the reference frame of the cutout in the given directions.
+ *
+ * @return a copy of this instance which has been inset
+ * @hide
+ */
+ public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
+ if (mBoundingRect.isEmpty()
+ || insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
+ return this;
+ }
+
+ Rect safeInsets = new Rect(mSafeInsets);
+ Rect boundingRect = new Rect(mBoundingRect);
+ ArrayList<Point> boundingPolygon = new ArrayList<>();
+ getBoundingPolygon(boundingPolygon);
+
+ // Note: it's not really well defined what happens when the inset is negative, because we
+ // don't know if the safe inset needs to expand in general.
+ if (insetTop > 0 || safeInsets.top > 0) {
+ safeInsets.top = atLeastZero(safeInsets.top - insetTop);
+ }
+ if (insetBottom > 0 || safeInsets.bottom > 0) {
+ safeInsets.bottom = atLeastZero(safeInsets.bottom - insetBottom);
+ }
+ if (insetLeft > 0 || safeInsets.left > 0) {
+ safeInsets.left = atLeastZero(safeInsets.left - insetLeft);
+ }
+ if (insetRight > 0 || safeInsets.right > 0) {
+ safeInsets.right = atLeastZero(safeInsets.right - insetRight);
+ }
+
+ boundingRect.offset(-insetLeft, -insetTop);
+ offset(boundingPolygon, -insetLeft, -insetTop);
+
+ return new DisplayCutout(safeInsets, boundingRect, boundingPolygon);
+ }
+
+ /**
+ * Calculates the safe insets relative to the given reference frame.
+ *
+ * @return a copy of this instance with the safe insets calculated
+ * @hide
+ */
+ public DisplayCutout calculateRelativeTo(Rect frame) {
+ if (mBoundingRect.isEmpty() || !Rect.intersects(frame, mBoundingRect)) {
+ return NO_CUTOUT;
+ }
+
+ Rect boundingRect = new Rect(mBoundingRect);
+ ArrayList<Point> boundingPolygon = new ArrayList<>();
+ getBoundingPolygon(boundingPolygon);
+
+ return DisplayCutout.calculateRelativeTo(frame, boundingRect, boundingPolygon);
+ }
+
+ private static DisplayCutout calculateRelativeTo(Rect frame, Rect boundingRect,
+ ArrayList<Point> boundingPolygon) {
+ Rect safeRect = new Rect();
+ int bestArea = 0;
+ int bestVariant = 0;
+ for (int variant = ROTATION_0; variant <= ROTATION_270; variant++) {
+ int area = calculateInsetVariantArea(frame, boundingRect, variant, safeRect);
+ if (bestArea < area) {
+ bestArea = area;
+ bestVariant = variant;
+ }
+ }
+ calculateInsetVariantArea(frame, boundingRect, bestVariant, safeRect);
+ if (safeRect.isEmpty()) {
+ // The entire frame overlaps with the cutout.
+ safeRect.set(0, frame.height(), 0, 0);
+ } else {
+ // Convert safeRect to insets relative to frame. We're reusing the rect here to avoid
+ // an allocation.
+ safeRect.set(
+ Math.max(0, safeRect.left - frame.left),
+ Math.max(0, safeRect.top - frame.top),
+ Math.max(0, frame.right - safeRect.right),
+ Math.max(0, frame.bottom - safeRect.bottom));
+ }
+
+ boundingRect.offset(-frame.left, -frame.top);
+ offset(boundingPolygon, -frame.left, -frame.top);
+
+ return new DisplayCutout(safeRect, boundingRect, boundingPolygon);
+ }
+
+ private static int calculateInsetVariantArea(Rect frame, Rect boundingRect, int variant,
+ Rect outSafeRect) {
+ switch (variant) {
+ case ROTATION_0:
+ outSafeRect.set(frame.left, frame.top, frame.right, boundingRect.top);
+ break;
+ case ROTATION_90:
+ outSafeRect.set(frame.left, frame.top, boundingRect.left, frame.bottom);
+ break;
+ case ROTATION_180:
+ outSafeRect.set(frame.left, boundingRect.bottom, frame.right, frame.bottom);
+ break;
+ case ROTATION_270:
+ outSafeRect.set(boundingRect.right, frame.top, frame.right, frame.bottom);
+ break;
+ }
+
+ return outSafeRect.isEmpty() ? 0 : outSafeRect.width() * outSafeRect.height();
+ }
+
+ private static int atLeastZero(int value) {
+ return value < 0 ? 0 : value;
+ }
+
+ private static void offset(ArrayList<Point> points, int dx, int dy) {
+ for (int i = 0; i < points.size(); i++) {
+ points.get(i).offset(dx, dy);
+ }
+ }
+
+ /**
+ * Creates an instance from a bounding polygon.
+ *
+ * @hide
+ */
+ public static DisplayCutout fromBoundingPolygon(List<Point> points) {
+ Rect boundingRect = new Rect(Integer.MAX_VALUE, Integer.MAX_VALUE,
+ Integer.MIN_VALUE, Integer.MIN_VALUE);
+ ArrayList<Point> boundingPolygon = new ArrayList<>();
+
+ for (int i = 0; i < points.size(); i++) {
+ Point point = points.get(i);
+ boundingRect.left = Math.min(boundingRect.left, point.x);
+ boundingRect.right = Math.max(boundingRect.right, point.x);
+ boundingRect.top = Math.min(boundingRect.top, point.y);
+ boundingRect.bottom = Math.max(boundingRect.bottom, point.y);
+ boundingPolygon.add(new Point(point));
+ }
+
+ return new DisplayCutout(ZERO_RECT, boundingRect, boundingPolygon);
+ }
+
+ /**
+ * Helper class for passing {@link DisplayCutout} through binder.
+ *
+ * Needed, because {@code readFromParcel} cannot be used with immutable classes.
+ *
+ * @hide
+ */
+ public static final class ParcelableWrapper implements Parcelable {
+
+ private DisplayCutout mInner;
+
+ public ParcelableWrapper() {
+ this(NO_CUTOUT);
+ }
+
+ public ParcelableWrapper(DisplayCutout cutout) {
+ mInner = cutout;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ if (mInner == NO_CUTOUT) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(1);
+ out.writeTypedObject(mInner.mSafeInsets, flags);
+ out.writeTypedObject(mInner.mBoundingRect, flags);
+ out.writeTypedList(mInner.mBoundingPolygon, flags);
+ }
+ }
+
+ /**
+ * Similar to {@link Creator#createFromParcel(Parcel)}, but reads into an existing
+ * instance.
+ *
+ * Needed for AIDL out parameters.
+ */
+ public void readFromParcel(Parcel in) {
+ mInner = readCutout(in);
+ }
+
+ public static final Creator<ParcelableWrapper> CREATOR = new Creator<ParcelableWrapper>() {
+ @Override
+ public ParcelableWrapper createFromParcel(Parcel in) {
+ return new ParcelableWrapper(readCutout(in));
+ }
+
+ @Override
+ public ParcelableWrapper[] newArray(int size) {
+ return new ParcelableWrapper[size];
+ }
+ };
+
+ private static DisplayCutout readCutout(Parcel in) {
+ if (in.readInt() == 0) {
+ return NO_CUTOUT;
+ }
+
+ ArrayList<Point> boundingPolygon = new ArrayList<>();
+
+ Rect safeInsets = in.readTypedObject(Rect.CREATOR);
+ Rect boundingRect = in.readTypedObject(Rect.CREATOR);
+ in.readTypedList(boundingPolygon, Point.CREATOR);
+
+ return new DisplayCutout(safeInsets, boundingRect, boundingPolygon);
+ }
+
+ public DisplayCutout get() {
+ return mInner;
+ }
+
+ public void set(ParcelableWrapper cutout) {
+ mInner = cutout.get();
+ }
+
+ public void set(DisplayCutout cutout) {
+ mInner = cutout;
+ }
+
+ @Override
+ public int hashCode() {
+ return mInner.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ParcelableWrapper
+ && mInner.equals(((ParcelableWrapper) o).mInner);
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(mInner);
+ }
+ }
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index aa2f1c1a9e24..cf059104e373 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -55,8 +55,8 @@ public class SurfaceControl {
private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
boolean allLayers, boolean useIdentityTransform);
- private static native void nativeCaptureLayers(IBinder layerHandleToken, Surface consumer,
- int rotation);
+ private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
+ Rect sourceCrop, float frameScale);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
@@ -1177,14 +1177,16 @@ public class SurfaceControl {
* Captures a layer and its children into the provided {@link Surface}.
*
* @param layerHandleToken The root layer to capture.
- * @param consumer The {@link Surface} to capture the layer into.
- * @param rotation Apply a custom clockwise rotation to the screenshot, i.e.
- * Surface.ROTATION_0,90,180,270. Surfaceflinger will always capture in its
- * native portrait orientation by default, so this is useful for returning
- * captures that are independent of device orientation.
+ * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+ * Rect()' or null if no cropping is desired.
+ * @param frameScale The desired scale of the returned buffer; the raw
+ * screen will be scaled up/down.
+ *
+ * @return Returns a GraphicBuffer that contains the layer capture.
*/
- public static void captureLayers(IBinder layerHandleToken, Surface consumer, int rotation) {
- nativeCaptureLayers(layerHandleToken, consumer, rotation);
+ public static GraphicBuffer captureLayers(IBinder layerHandleToken, Rect sourceCrop,
+ float frameScale) {
+ return nativeCaptureLayers(layerHandleToken, sourceCrop, frameScale);
}
public static class Transaction implements Closeable {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e36a2989afbc..345e30030534 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2929,6 +2929,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG3_TEMPORARY_DETACH
* 1 PFLAG3_NO_REVEAL_ON_FOCUS
* 1 PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT
+ * 1 PFLAG3_SCREEN_READER_FOCUSABLE
* |-------|-------|-------|-------|
*/
@@ -3209,6 +3210,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
static final int PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT = 0x8000000;
+ /**
+ * Works like focusable for screen readers, but without the side effects on input focus.
+ * @see #setScreenReaderFocusable(boolean)
+ */
+ private static final int PFLAG3_SCREEN_READER_FOCUSABLE = 0x10000000;
+
/* End of masks for mPrivateFlags3 */
/**
@@ -5344,6 +5351,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setDefaultFocusHighlightEnabled(a.getBoolean(attr, true));
}
break;
+ case R.styleable.View_screenReaderFocusable:
+ if (a.peekValue(attr) != null) {
+ setScreenReaderFocusable(a.getBoolean(attr, false));
+ }
+ break;
}
}
@@ -8394,6 +8406,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
info.setEnabled(isEnabled());
info.setClickable(isClickable());
info.setFocusable(isFocusable());
+ info.setScreenReaderFocusable(isScreenReaderFocusable());
info.setFocused(isFocused());
info.setAccessibilityFocused(isAccessibilityFocused());
info.setSelected(isSelected());
@@ -10350,6 +10363,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns whether the view should be treated as a focusable unit by screen reader
+ * accessibility tools.
+ * @see #setScreenReaderFocusable(boolean)
+ *
+ * @return Whether the view should be treated as a focusable unit by screen reader.
+ */
+ public boolean isScreenReaderFocusable() {
+ return (mPrivateFlags3 & PFLAG3_SCREEN_READER_FOCUSABLE) != 0;
+ }
+
+ /**
+ * When screen readers (one type of accessibility tool) decide what should be read to the
+ * user, they typically look for input focusable ({@link #isFocusable()}) parents of
+ * non-focusable text items, and read those focusable parents and their non-focusable children
+ * as a unit. In some situations, this behavior is desirable for views that should not take
+ * input focus. Setting an item to be screen reader focusable requests that the view be
+ * treated as a unit by screen readers without any effect on input focusability. The default
+ * value of {@code false} lets screen readers use other signals, like focusable, to determine
+ * how to group items.
+ *
+ * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader
+ * accessibility tools.
+ */
+ public void setScreenReaderFocusable(boolean screenReaderFocusable) {
+ int pflags3 = mPrivateFlags3;
+ if (screenReaderFocusable) {
+ pflags3 |= PFLAG3_SCREEN_READER_FOCUSABLE;
+ } else {
+ pflags3 &= ~PFLAG3_SCREEN_READER_FOCUSABLE;
+ }
+
+ if (pflags3 != mPrivateFlags3) {
+ mPrivateFlags3 = pflags3;
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+ }
+
+ /**
* Find the nearest view in the specified direction that can take focus.
* This does not actually give focus to that view.
*
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 929beaea42b8..122df934111d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2591,7 +2591,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
- // If the event is targeting accessiiblity focus we give it to the
+ // If the event is targeting accessibility focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e9509b798c76..1c9d86398a8d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1563,7 +1563,7 @@ public final class ViewRootImpl implements ViewParent,
mLastWindowInsets = new WindowInsets(contentInsets,
null /* windowDecorInsets */, stableInsets,
mContext.getResources().getConfiguration().isScreenRound(),
- mAttachInfo.mAlwaysConsumeNavBar);
+ mAttachInfo.mAlwaysConsumeNavBar, null /* displayCutout */);
}
return mLastWindowInsets;
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 750931ab661c..df124ac5be28 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,6 +17,7 @@
package android.view;
+import android.annotation.NonNull;
import android.graphics.Rect;
/**
@@ -36,6 +37,7 @@ public final class WindowInsets {
private Rect mStableInsets;
private Rect mTempRect;
private boolean mIsRound;
+ private DisplayCutout mDisplayCutout;
/**
* In multi-window we force show the navigation bar. Because we don't want that the surface size
@@ -47,6 +49,7 @@ public final class WindowInsets {
private boolean mSystemWindowInsetsConsumed = false;
private boolean mWindowDecorInsetsConsumed = false;
private boolean mStableInsetsConsumed = false;
+ private boolean mCutoutConsumed = false;
private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
@@ -59,12 +62,12 @@ public final class WindowInsets {
public static final WindowInsets CONSUMED;
static {
- CONSUMED = new WindowInsets(null, null, null, false, false);
+ CONSUMED = new WindowInsets(null, null, null, false, false, null);
}
/** @hide */
public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
- boolean isRound, boolean alwaysConsumeNavBar) {
+ boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
mSystemWindowInsetsConsumed = systemWindowInsets == null;
mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets;
@@ -76,6 +79,9 @@ public final class WindowInsets {
mIsRound = isRound;
mAlwaysConsumeNavBar = alwaysConsumeNavBar;
+
+ mCutoutConsumed = displayCutout == null;
+ mDisplayCutout = mCutoutConsumed ? DisplayCutout.NO_CUTOUT : displayCutout;
}
/**
@@ -92,11 +98,13 @@ public final class WindowInsets {
mStableInsetsConsumed = src.mStableInsetsConsumed;
mIsRound = src.mIsRound;
mAlwaysConsumeNavBar = src.mAlwaysConsumeNavBar;
+ mDisplayCutout = src.mDisplayCutout;
+ mCutoutConsumed = src.mCutoutConsumed;
}
/** @hide */
public WindowInsets(Rect systemWindowInsets) {
- this(systemWindowInsets, null, null, false, false);
+ this(systemWindowInsets, null, null, false, false, null);
}
/**
@@ -260,9 +268,34 @@ public final class WindowInsets {
* @return true if any inset values are nonzero
*/
public boolean hasInsets() {
- return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets();
+ return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets()
+ || mDisplayCutout.hasCutout();
+ }
+
+ /**
+ * @return the display cutout
+ * @see DisplayCutout
+ * @hide pending API
+ */
+ @NonNull
+ public DisplayCutout getDisplayCutout() {
+ return mDisplayCutout;
+ }
+
+ /**
+ * Returns a copy of this WindowInsets with the cutout fully consumed.
+ *
+ * @return A modified copy of this WindowInsets
+ * @hide pending API
+ */
+ public WindowInsets consumeCutout() {
+ final WindowInsets result = new WindowInsets(this);
+ result.mDisplayCutout = DisplayCutout.NO_CUTOUT;
+ result.mCutoutConsumed = true;
+ return result;
}
+
/**
* Check if these insets have been fully consumed.
*
@@ -277,7 +310,8 @@ public final class WindowInsets {
* @return true if the insets have been fully consumed.
*/
public boolean isConsumed() {
- return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed;
+ return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed
+ && mCutoutConsumed;
}
/**
@@ -495,7 +529,9 @@ public final class WindowInsets {
public String toString() {
return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
+ " windowDecorInsets=" + mWindowDecorInsets
- + " stableInsets=" + mStableInsets +
- (isRound() ? " round}" : "}");
+ + " stableInsets=" + mStableInsets
+ + (mDisplayCutout.hasCutout() ? " cutout=" + mDisplayCutout : "")
+ + (isRound() ? " round" : "")
+ + "}";
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index eb5fc92e5ccc..905c0715ecb8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -604,8 +604,10 @@ public interface WindowManager extends ViewManager {
public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16;
/**
- * Window type: panel that slides out from under the status bar
- * In multiuser systems shows on all users' windows.
+ * Window type: panel that slides out from over the status bar
+ * In multiuser systems shows on all users' windows. These windows
+ * are displayed on top of the stauts bar and any {@link #TYPE_STATUS_BAR_PANEL}
+ * windows.
* @hide
*/
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index d7851171cd67..da5a1cd67922 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -23,8 +23,6 @@ import android.util.LongArray;
import android.util.LongSparseArray;
import android.util.SparseArray;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.util.ArrayList;
import java.util.List;
@@ -33,8 +31,7 @@ import java.util.List;
* It is updated when windows change or nodes change.
* @hide
*/
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class AccessibilityCache {
+public class AccessibilityCache {
private static final String LOG_TAG = "AccessibilityCache";
@@ -329,6 +326,8 @@ public final class AccessibilityCache {
final long oldParentId = oldInfo.getParentNodeId();
if (info.getParentNodeId() != oldParentId) {
clearSubTreeLocked(windowId, oldParentId);
+ } else {
+ oldInfo.recycle();
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index c3d6c695982d..d890f329dd31 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -28,6 +28,8 @@ import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -86,6 +88,12 @@ public final class AccessibilityInteractionClient
private static final LongSparseArray<AccessibilityInteractionClient> sClients =
new LongSparseArray<>();
+ private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
+ new SparseArray<>();
+
+ private static AccessibilityCache sAccessibilityCache =
+ new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher());
+
private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
private final Object mInstanceLock = new Object();
@@ -100,12 +108,6 @@ public final class AccessibilityInteractionClient
private Message mSameThreadMessage;
- private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
- new SparseArray<>();
-
- private static final AccessibilityCache sAccessibilityCache =
- new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher());
-
/**
* @return The client for the current thread.
*/
@@ -133,6 +135,50 @@ public final class AccessibilityInteractionClient
}
}
+ /**
+ * Gets a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ * @return The cached connection if such.
+ */
+ public static IAccessibilityServiceConnection getConnection(int connectionId) {
+ synchronized (sConnectionCache) {
+ return sConnectionCache.get(connectionId);
+ }
+ }
+
+ /**
+ * Adds a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ * @param connection The connection.
+ */
+ public static void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
+ synchronized (sConnectionCache) {
+ sConnectionCache.put(connectionId, connection);
+ }
+ }
+
+ /**
+ * Removes a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ */
+ public static void removeConnection(int connectionId) {
+ synchronized (sConnectionCache) {
+ sConnectionCache.remove(connectionId);
+ }
+ }
+
+ /**
+ * This method is only for testing. Replacing the cache is a generally terrible idea, but
+ * tests need to be able to verify this class's interactions with the cache
+ */
+ @VisibleForTesting
+ public static void setCache(AccessibilityCache cache) {
+ sAccessibilityCache = cache;
+ }
+
private AccessibilityInteractionClient() {
/* reducing constructor visibility */
}
@@ -300,7 +346,7 @@ public final class AccessibilityInteractionClient
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, bypassCache);
if (infos != null && !infos.isEmpty()) {
for (int i = 1; i < infos.size(); i++) {
infos.get(i).recycle();
@@ -356,7 +402,7 @@ public final class AccessibilityInteractionClient
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false);
return infos;
}
}
@@ -409,7 +455,7 @@ public final class AccessibilityInteractionClient
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false);
return infos;
}
}
@@ -460,7 +506,7 @@ public final class AccessibilityInteractionClient
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false);
return info;
}
} else {
@@ -509,7 +555,7 @@ public final class AccessibilityInteractionClient
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false);
return info;
}
} else {
@@ -731,13 +777,17 @@ public final class AccessibilityInteractionClient
*
* @param info The info.
* @param connectionId The id of the connection to the system.
+ * @param bypassCache Whether or not to bypass the cache. The node is added to the cache if
+ * this value is {@code false}
*/
private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info,
- int connectionId) {
+ int connectionId, boolean bypassCache) {
if (info != null) {
info.setConnectionId(connectionId);
info.setSealed(true);
- sAccessibilityCache.add(info);
+ if (!bypassCache) {
+ sAccessibilityCache.add(info);
+ }
}
}
@@ -746,14 +796,16 @@ public final class AccessibilityInteractionClient
*
* @param infos The {@link AccessibilityNodeInfo}s.
* @param connectionId The id of the connection to the system.
+ * @param bypassCache Whether or not to bypass the cache. The nodes are added to the cache if
+ * this value is {@code false}
*/
private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
- int connectionId) {
+ int connectionId, boolean bypassCache) {
if (infos != null) {
final int infosCount = infos.size();
for (int i = 0; i < infosCount; i++) {
AccessibilityNodeInfo info = infos.get(i);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, bypassCache);
}
}
}
@@ -773,41 +825,6 @@ public final class AccessibilityInteractionClient
}
/**
- * Gets a cached accessibility service connection.
- *
- * @param connectionId The connection id.
- * @return The cached connection if such.
- */
- public IAccessibilityServiceConnection getConnection(int connectionId) {
- synchronized (sConnectionCache) {
- return sConnectionCache.get(connectionId);
- }
- }
-
- /**
- * Adds a cached accessibility service connection.
- *
- * @param connectionId The connection id.
- * @param connection The connection.
- */
- public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
- synchronized (sConnectionCache) {
- sConnectionCache.put(connectionId, connection);
- }
- }
-
- /**
- * Removes a cached accessibility service connection.
- *
- * @param connectionId The connection id.
- */
- public void removeConnection(int connectionId) {
- synchronized (sConnectionCache) {
- sConnectionCache.remove(connectionId);
- }
- }
-
- /**
* Checks whether the infos are a fully connected tree with no duplicates.
*
* @param infos The result list to check.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 9bdd3ffc9fb2..faea9200c522 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -635,6 +635,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000;
+ private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x0080000;
+
private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x0100000;
/**
@@ -2321,6 +2323,37 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note
+ * that {@code false} indicates that it is not explicitly marked, not that the node is not
+ * a focusable unit. Screen readers should generally used other signals, such as
+ * {@link #isFocusable()}, or the presence of text in a node, to determine what should receive
+ * focus.
+ *
+ * @return {@code true} if the node is specifically marked as a focusable unit for screen
+ * readers, {@code false} otherwise.
+ *
+ * @see View#isScreenReaderFocusable()
+ */
+ public boolean isScreenReaderFocusable() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE);
+ }
+
+ /**
+ * Sets whether the node should be considered a focusable unit by a screen reader.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param screenReaderFocusable {@code true} if the node is a focusable unit for screen readers,
+ * {@code false} otherwise.
+ */
+ public void setScreenReaderFocusable(boolean screenReaderFocusable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
+ }
+
+ /**
* Returns whether the node's text represents a hint for the user to enter text. It should only
* be {@code true} if the node has editable text.
*
diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java
index 8d9a1cf9eee5..76dcc9bba91c 100644
--- a/core/java/com/android/internal/content/ReferrerIntent.java
+++ b/core/java/com/android/internal/content/ReferrerIntent.java
@@ -19,6 +19,8 @@ package com.android.internal.content;
import android.content.Intent;
import android.os.Parcel;
+import java.util.Objects;
+
/**
* Subclass of Intent that also contains referrer (as a package name) information.
*/
@@ -48,4 +50,21 @@ public class ReferrerIntent extends Intent {
return new ReferrerIntent[size];
}
};
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ReferrerIntent)) {
+ return false;
+ }
+ final ReferrerIntent other = (ReferrerIntent) obj;
+ return filterEquals(other) && Objects.equals(mReferrer, other.mReferrer);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + filterHashCode();
+ result = 31 * result + Objects.hashCode(mReferrer);
+ return result;
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6ede72d49806..56d0bb229b80 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6020,6 +6020,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public Timer getMulticastWakelockStats() {
+ return mWifiMulticastTimer;
+ }
+
+ @Override
public ArrayMap<String, ? extends BatteryStats.Timer> getSyncStats() {
return mSyncStats.getMap();
}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3ad4da6b6580..421e0de52cc0 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -517,23 +517,7 @@ static jint nativeAttachAndQueueBuffer(JNIEnv *env, jclass clazz, jlong nativeOb
jobject graphicBuffer) {
Surface* surface = reinterpret_cast<Surface*>(nativeObject);
sp<GraphicBuffer> bp = graphicBufferForJavaObject(env, graphicBuffer);
- if (bp == nullptr) {
- return BAD_VALUE;
- }
- int err = ((ANativeWindow*)surface)->perform(surface, NATIVE_WINDOW_API_CONNECT,
- NATIVE_WINDOW_API_CPU);
- if (err != OK) {
- return err;
- }
- err = surface->attachBuffer(bp->getNativeBuffer());
- if (err != OK) {
- return err;
- }
- err = ((ANativeWindow*)surface)->queueBuffer(surface, bp->getNativeBuffer(), -1);
- if (err != OK) {
- return err;
- }
- err = surface->disconnect(NATIVE_WINDOW_API_CPU);
+ int err = Surface::attachAndQueueBuffer(surface, bp);
return err;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index bb1bfad26dc3..8c968a2a7083 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -167,7 +167,7 @@ static jobject nativeScreenshotToBuffer(JNIEnv* env, jclass clazz,
maxLayer = INT32_MAX;
}
sp<GraphicBuffer> buffer;
- status_t res = ScreenshotClient::captureToBuffer(displayToken,
+ status_t res = ScreenshotClient::capture(displayToken,
sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform,
rotation, &buffer);
if (res != NO_ERROR) {
@@ -201,15 +201,18 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
maxLayer = INT32_MAX;
}
- res = screenshot->update(displayToken, sourceCrop, width, height,
- minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation));
+ sp<GraphicBuffer> buffer;
+ res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
+ minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer);
if (res != NO_ERROR) {
return NULL;
}
SkColorType colorType;
SkAlphaType alphaType;
- switch (screenshot->getFormat()) {
+
+ PixelFormat format = buffer->getPixelFormat();
+ switch (format) {
case PIXEL_FORMAT_RGBX_8888: {
colorType = kRGBA_8888_SkColorType;
alphaType = kOpaque_SkAlphaType;
@@ -235,75 +238,71 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
}
}
- sk_sp<SkColorSpace> colorSpace;
- if (screenshot->getDataSpace() == HAL_DATASPACE_DISPLAY_P3) {
- colorSpace = SkColorSpace::MakeRGB(
- SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
- } else {
- colorSpace = SkColorSpace::MakeSRGB();
- }
-
- SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(),
- screenshot->getHeight(),
- colorType,
- alphaType,
- colorSpace);
-
- const size_t rowBytes =
- screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
+ SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
+ colorType, alphaType,
+ SkColorSpace::MakeSRGB());
- if (!screenshotInfo.width() || !screenshotInfo.height()) {
- return NULL;
- }
-
- auto bitmap = new Bitmap(
- (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
- screenshotInfo, rowBytes);
- screenshot.release();
- bitmap->setImmutable();
- return bitmap::createBitmap(env, bitmap,
- android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
+ auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info));
+ return bitmap::createBitmap(env, bitmap.release(),
+ android::bitmap::kBitmapCreateFlag_Premultiplied, NULL);
}
static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
jobject surfaceObj, jobject sourceCropObj, jint width, jint height,
jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
- if (displayToken != NULL) {
- sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
- if (consumer != NULL) {
- int left = env->GetIntField(sourceCropObj, gRectClassInfo.left);
- int top = env->GetIntField(sourceCropObj, gRectClassInfo.top);
- int right = env->GetIntField(sourceCropObj, gRectClassInfo.right);
- int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
- Rect sourceCrop(left, top, right, bottom);
-
- if (allLayers) {
- minLayer = INT32_MIN;
- maxLayer = INT32_MAX;
- }
- ScreenshotClient::capture(displayToken,
- consumer->getIGraphicBufferProducer(), sourceCrop,
- width, height, minLayer, maxLayer,
- useIdentityTransform);
- }
+ if (displayToken == NULL) {
+ return;
}
+
+ sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
+ if (consumer == NULL) {
+ return;
+ }
+
+ Rect sourceCrop;
+ if (sourceCropObj != NULL) {
+ sourceCrop = rectFromObj(env, sourceCropObj);
+ }
+
+ if (allLayers) {
+ minLayer = INT32_MIN;
+ maxLayer = INT32_MAX;
+ }
+
+ sp<GraphicBuffer> buffer;
+ ScreenshotClient::capture(displayToken, sourceCrop, width, height, minLayer, maxLayer,
+ useIdentityTransform, 0, &buffer);
+
+ Surface::attachAndQueueBuffer(consumer.get(), buffer);
}
-static void nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
- jobject surfaceObj, int rotation) {
+static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken,
+ jobject sourceCropObj, jfloat frameScale) {
sp<IBinder> layerHandle = ibinderForJavaObject(env, layerHandleToken);
if (layerHandle == NULL) {
- return;
+ return NULL;
}
- sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
- if (consumer == NULL) {
- return;
+ Rect sourceCrop;
+ if (sourceCropObj != NULL) {
+ sourceCrop = rectFromObj(env, sourceCropObj);
}
- ScreenshotClient::captureLayers(layerHandle, consumer->getIGraphicBufferProducer(), rotation);
+ sp<GraphicBuffer> buffer;
+ status_t res = ScreenshotClient::captureLayers(layerHandle, sourceCrop, frameScale, &buffer);
+ if (res != NO_ERROR) {
+ return NULL;
+ }
+
+ return env->CallStaticObjectMethod(gGraphicBufferClassInfo.clazz,
+ gGraphicBufferClassInfo.builder,
+ buffer->getWidth(),
+ buffer->getHeight(),
+ buffer->getPixelFormat(),
+ (jint)buffer->getUsage(),
+ (jlong)buffer.get());
}
static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
@@ -975,7 +974,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeScreenshotToBuffer",
"(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;",
(void*)nativeScreenshotToBuffer },
- {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/view/Surface;I)V",
+ {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
(void*)nativeCaptureLayers },
};
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 0a3344fe13f1..cff187992fdb 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -395,6 +395,12 @@ message SystemProto {
};
repeated WakeupReason wakeup_reason = 22;
+ message WifiMulticastWakelockTotal {
+ optional int64 duration_ms = 1;
+ optional int32 count = 2;
+ }
+ optional WifiMulticastWakelockTotal wifi_multicast_wakelock_total = 23;
+
message WifiSignalStrength {
enum Name {
NONE = 0;
@@ -406,7 +412,7 @@ message SystemProto {
optional Name name = 1;
optional TimerProto total = 2;
};
- repeated WifiSignalStrength wifi_signal_strength = 23;
+ repeated WifiSignalStrength wifi_signal_strength = 24;
message WifiState {
enum Name {
@@ -422,7 +428,7 @@ message SystemProto {
optional Name name = 1;
optional TimerProto total = 2;
};
- repeated WifiState wifi_state = 24;
+ repeated WifiState wifi_state = 25;
message WifiSupplicantState {
enum Name {
@@ -443,7 +449,7 @@ message SystemProto {
optional Name name = 1;
optional TimerProto total = 2;
};
- repeated WifiSupplicantState wifi_supplicant_state = 25;
+ repeated WifiSupplicantState wifi_supplicant_state = 26;
}
message TimerProto {
@@ -775,4 +781,12 @@ message UidProto {
optional TimerProto background_scan = 4;
};
optional Wifi wifi = 27;
+
+ // WiFi Multicast Wakelock
+ // This timer tracks the duration and count for the app to request the
+ // wakelock for wifi multicast traffic.
+ // This wakelock disables the filtering of multicast packets to reach the host
+ // processor, and results in a power penalty.
+ // It is useful to monitor the applications resulting in that
+ optional TimerProto wifi_multicast_wakelock = 28;
}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 5d5aea2a12cd..322914061509 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -281,6 +281,7 @@ message GlobalSettingsProto {
optional SettingProto bluetooth_pbap_client_priority_prefix = 209;
optional SettingProto bluetooth_sap_priority_prefix = 210;
optional SettingProto bluetooth_pan_priority_prefix = 211;
+ optional SettingProto bluetooth_hearing_aid_priority_prefix = 345;
optional SettingProto activity_manager_constants = 317;
optional SettingProto device_idle_constants = 212;
optional SettingProto device_idle_constants_watch = 213;
@@ -387,7 +388,7 @@ message GlobalSettingsProto {
optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
optional SettingProto notification_snooze_options = 341;
- // Next tag = 345;
+ // Next tag = 346;
}
message SecureSettingsProto {
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0eefec91e390..9a0eafdb7c77 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3021,6 +3021,12 @@
<!-- Whether this View should use a default focus highlight when it gets focused but
doesn't have {@link android.R.attr#state_focused} defined in its background. -->
<attr name="defaultFocusHighlightEnabled" format="boolean" />
+
+ <!-- Whether this view should be treated as a focusable unit by screen reader accessibility
+ tools. See {@link android.view.View#setScreenReaderFocusable(boolean)}. The default
+ value, {@code false}, leaves the screen reader to consider other signals, such as
+ focusability or the presence of text, to decide what it focus.-->
+ <attr name="screenReaderFocusable" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c10461643406..65a7ec643078 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1955,7 +1955,7 @@
<string name="config_dozeLongPressSensorType" translatable="false"></string>
<!-- Control whether the always on display mode is available. This should only be enabled on
- devices where the display has be tuned to be power efficient in DOZE and/or DOZE_SUSPEND
+ devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
states. -->
<bool name="config_dozeAlwaysOnDisplayAvailable">false</bool>
@@ -3174,6 +3174,10 @@
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">2dp</dimen>
+ <!-- Controls whether system buttons use all caps for text -->
+ <bool name="config_buttonTextAllCaps">true</bool>
+ <!-- Name of the font family used for system buttons -->
+ <string name="config_fontFamilyButton">@string/font_family_button_material</string>
<string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 946216c06a01..258ea387a696 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -266,6 +266,8 @@
<dimen name="alert_dialog_round_padding">27dip</dimen>
<!-- Dialog title height -->
<dimen name="alert_dialog_title_height">64dip</dimen>
+ <!-- Dialog button bar width -->
+ <dimen name="alert_dialog_button_bar_width">64dp</dimen>
<!-- Dialog button bar height -->
<dimen name="alert_dialog_button_bar_height">48dip</dimen>
<!-- Leanback dialog vertical margin -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d3533fe6e79f..e370786ee1c1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2852,9 +2852,12 @@
<public name="compileSdkVersion" />
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="compileSdkVersionCodename" />
+ <public name="screenReaderFocusable" />
</public-group>
<public-group type="style" first-id="0x010302e0">
+ <public name="Widget.DeviceDefault.Button.Colored" />
+ <public name="Widget.DeviceDefault.Button.Borderless.Colored" />
</public-group>
<public-group type="id" first-id="0x01020044">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e4310e1070c8..2f1d679744c4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2853,7 +2853,7 @@
<string name="unsupported_display_size_show">Always show</string>
<!-- [CHAR LIMIT=200] Unsupported compile SDK dialog: message. Shown when an app may not be compatible with the device's current version of Android. -->
- <string name="unsupported_compile_sdk_message"><xliff:g id="app_name">%1$s</xliff:g> was built for preview version %2$s of the Android OS and may behave unexpectedly. An updated version of the app may be available.</string>
+ <string name="unsupported_compile_sdk_message"><xliff:g id="app_name">%1$s</xliff:g> was built for an incompatible version of the Android OS and may behave unexpectedly. An updated version of the app may be available.</string>
<!-- [CHAR LIMIT=50] Unsupported compile SDK dialog: check box label. -->
<string name="unsupported_compile_sdk_show">Always show</string>
<!-- [CHAR LIMIT=50] Unsupported compile SDK dialog: label for button to check for an app update. -->
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 93a5264a0e26..189b3b7b0481 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -37,6 +37,10 @@ easier.
<style name="Widget.DeviceDefault.Button.Small" parent="Widget.Material.Button.Small"/>
<style name="Widget.DeviceDefault.Button.Inset" parent="Widget.Material.Button.Inset"/>
<style name="Widget.DeviceDefault.Button.Toggle" parent="Widget.Material.Button.Toggle"/>
+ <style name="Widget.DeviceDefault.Button.Colored" parent="Widget.Material.Button.Colored">
+ <item name="textAppearance">?attr/textAppearanceButton</item>
+ <item name="textColor">@color/btn_colored_text_material</item>
+ </style>
<style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView"/>
<style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
<style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/>
@@ -77,6 +81,15 @@ easier.
<style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode"/>
<style name="Widget.DeviceDefault.ActionBar" parent="Widget.Material.ActionBar"/>
<style name="Widget.DeviceDefault.Button.Borderless" parent="Widget.Material.Button.Borderless"/>
+ <!-- Colored borderless ink button -->
+ <style name="Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored</item>
+ </style>
+ <!-- Alert dialog button bar button -->
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog" parent="Widget.DeviceDefault.Button.Borderless.Colored">
+ <item name="minWidth">@dimen/alert_dialog_button_bar_width</item>
+ <item name="minHeight">@dimen/alert_dialog_button_bar_height</item>
+ </style>
<style name="Widget.DeviceDefault.Tab" parent="Widget.Material.Tab"/>
<style name="Widget.DeviceDefault.CalendarView" parent="Widget.Material.CalendarView"/>
<style name="Widget.DeviceDefault.DatePicker" parent="Widget.Material.DatePicker"/>
@@ -211,7 +224,10 @@ easier.
<style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Material.SearchResult.Title"/>
<style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Material.SearchResult.Subtitle"/>
<style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget"/>
- <style name="TextAppearance.DeviceDefault.Widget.Button" parent="TextAppearance.Material.Widget.Button"/>
+ <style name="TextAppearance.DeviceDefault.Widget.Button" parent="TextAppearance.Material.Widget.Button">
+ <item name="fontFamily">@string/config_fontFamilyButton</item>
+ <item name="textAllCaps">@bool/config_buttonTextAllCaps</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Material.Widget.IconMenu.Item"/>
<style name="TextAppearance.DeviceDefault.Widget.TabWidget" parent="TextAppearance.Material.Widget.TabWidget"/>
<style name="TextAppearance.DeviceDefault.Widget.TextView" parent="TextAppearance.Material.Widget.TextView"/>
@@ -220,6 +236,9 @@ easier.
<style name="TextAppearance.DeviceDefault.Widget.DropDownItem" parent="TextAppearance.Material.Widget.DropDownItem"/>
<style name="TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Widget.TextView.SpinnerItem"/>
<style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText"/>
+ <style name="TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored" parent="TextAppearance.DeviceDefault.Widget.Button">
+ <item name="textColor">@color/btn_colored_borderless_text_material</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Material.Widget.PopupMenu"/>
<style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Material.Widget.PopupMenu.Large"/>
<style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Material.Widget.PopupMenu.Small"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2cd4dcb84a5f..67a499dd9ca6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -561,7 +561,7 @@ please see styles_device_defaults.xml.
<!-- Alert dialog button bar button -->
<style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Colored">
- <item name="minWidth">64dp</item>
+ <item name="minWidth">@dimen/alert_dialog_button_bar_width</item>
<item name="minHeight">@dimen/alert_dialog_button_bar_height</item>
</style>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 68d552367423..3fa0d3b542fc 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -73,8 +73,8 @@ easier.
<item name="buttonStyleSmall">@style/Widget.DeviceDefault.Button.Small</item>
<item name="buttonStyleInset">@style/Widget.DeviceDefault.Button.Inset</item>
-
<item name="buttonStyleToggle">@style/Widget.DeviceDefault.Button.Toggle</item>
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<item name="switchStyle">@style/Widget.DeviceDefault.CompoundButton.Switch</item>
<item name="borderlessButtonStyle">@style/Widget.DeviceDefault.Button.Borderless</item>
@@ -219,6 +219,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -232,6 +238,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -247,6 +259,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -261,6 +279,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -273,8 +297,13 @@ easier.
<item name="buttonBarStyle">@style/DeviceDefault.ButtonBar.AlertDialog</item>
<item name="borderlessButtonStyle">@style/Widget.DeviceDefault.Button.Borderless.Small</item>
+ <!-- Text styles -->
<item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
<item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
@@ -297,6 +326,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -309,6 +344,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -322,6 +363,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -351,6 +398,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -365,6 +418,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -377,6 +436,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -391,6 +456,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -404,6 +475,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -417,6 +494,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -430,6 +513,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -443,6 +532,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -456,6 +551,12 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
@@ -467,6 +568,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -478,6 +585,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -644,6 +757,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -656,6 +775,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -669,6 +794,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -684,6 +815,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -698,6 +835,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -711,11 +854,14 @@ easier.
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+ <!-- Button styles -->
<item name="buttonBarStyle">@style/DeviceDefault.Light.ButtonBar.AlertDialog</item>
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<item name="borderlessButtonStyle">@style/Widget.DeviceDefault.Light.Button.Borderless.Small</item>
<item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
<item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_light</item>
@@ -734,6 +880,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -746,6 +898,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -759,6 +917,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -798,6 +962,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -812,6 +982,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -824,6 +1000,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -838,6 +1020,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -851,6 +1039,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
@@ -862,6 +1056,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -873,6 +1073,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -896,6 +1102,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<!-- @hide DeviceDefault theme for a window that should use Settings theme colors
@@ -908,6 +1120,12 @@ easier.
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorControlNormal">?attr/textColorPrimary</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -930,6 +1148,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
@@ -942,6 +1166,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
@@ -954,6 +1184,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -966,6 +1202,12 @@ easier.
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
+ <!-- Text styles -->
+ <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+
+ <!-- Button styles -->
+ <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 51bfc209bf55..3f2a46a0e9ca 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -68,6 +68,7 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.TEST_GRANTED" />
+ <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" />
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 23d70b8dedd4..970a0f00bf5e 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Runs Frameworks Core Tests.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="FrameworksCoreTests.apk" />
+ <option name="test-file-name" value="BstatsTestApp.apk" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
diff --git a/core/tests/coretests/BstatsTestApp/Android.mk b/core/tests/coretests/BstatsTestApp/Android.mk
new file mode 100644
index 000000000000..628025751d77
--- /dev/null
+++ b/core/tests/coretests/BstatsTestApp/Android.mk
@@ -0,0 +1,32 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := BstatsTestApp
+LOCAL_CERTIFICATE := platform
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE) \ No newline at end of file
diff --git a/core/tests/coretests/BstatsTestApp/AndroidManifest.xml b/core/tests/coretests/BstatsTestApp/AndroidManifest.xml
new file mode 100644
index 000000000000..0cb5498a9c21
--- /dev/null
+++ b/core/tests/coretests/BstatsTestApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.coretests.apps.bstatstestapp">
+
+ <application>
+ <activity android:name=".TestActivity"
+ android:exported="true" />
+ <service android:name=".IsolatedTestService"
+ android:exported="true"
+ android:isolatedProcess="true" />
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java
new file mode 100644
index 000000000000..1f5f39730096
--- /dev/null
+++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.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.coretests.apps.bstatstestapp;
+
+import com.android.frameworks.coretests.aidl.ICmdReceiver;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Log;
+
+public class IsolatedTestService extends Service {
+ private static final String TAG = IsolatedTestService.class.getName();
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "onCreate called. myUid=" + Process.myUid());
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mReceiver.asBinder();
+ }
+
+ private ICmdReceiver mReceiver = new ICmdReceiver.Stub() {
+ @Override
+ public void doSomeWork(int durationMs) {
+ final long endTime = SystemClock.uptimeMillis() + durationMs;
+ double x;
+ double y;
+ double z;
+ while (SystemClock.uptimeMillis() <= endTime) {
+ x = 0.02;
+ x *= 1000;
+ y = x % 5;
+ z = Math.sqrt(y / 100);
+ }
+ };
+
+ @Override
+ public void finishHost() {
+ stopSelf();
+ }
+ };
+}
diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java
new file mode 100644
index 000000000000..87b14d934913
--- /dev/null
+++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java
@@ -0,0 +1,83 @@
+/*
+ * 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.coretests.apps.bstatstestapp;
+
+import com.android.frameworks.coretests.aidl.ICmdCallback;
+import com.android.frameworks.coretests.aidl.ICmdReceiver;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+public class TestActivity extends Activity {
+ private static final String TAG = TestActivity.class.getName();
+
+ private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ Log.d(TAG, "onCreate called");
+ notifyActivityLaunched();
+ }
+
+ private void notifyActivityLaunched() {
+ if (getIntent() == null) {
+ return;
+ }
+
+ final Bundle extras = getIntent().getExtras();
+ if (extras == null) {
+ return;
+ }
+ final ICmdCallback callback = ICmdCallback.Stub.asInterface(
+ extras.getBinder(EXTRA_KEY_CMD_RECEIVER));
+ try {
+ callback.onActivityLaunched(mReceiver.asBinder());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error occured while notifying the test: " + e);
+ }
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ Log.d(TAG, "finish called");
+ }
+
+ private ICmdReceiver mReceiver = new ICmdReceiver.Stub() {
+ @Override
+ public void doSomeWork(int durationMs) {
+ final long endTime = SystemClock.uptimeMillis() + durationMs;
+ double x;
+ double y;
+ double z;
+ while (SystemClock.uptimeMillis() <= endTime) {
+ x = 0.02;
+ x *= 1000;
+ y = x % 5;
+ z = Math.sqrt(y / 100);
+ }
+ };
+
+ @Override
+ public void finishHost() {
+ finish();
+ }
+ };
+}
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdCallback.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdCallback.aidl
new file mode 100644
index 000000000000..53a181acd5b6
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.frameworks.coretests.aidl;
+
+interface ICmdCallback {
+ void onActivityLaunched(IBinder receiver);
+} \ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdReceiver.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdReceiver.aidl
new file mode 100644
index 000000000000..c406570ecfae
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdReceiver.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.frameworks.coretests.aidl;
+
+interface ICmdReceiver {
+ void doSomeWork(int durationMs);
+ void finishHost();
+} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
new file mode 100644
index 000000000000..e0fcf0833232
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -0,0 +1,78 @@
+/*
+ * 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.app.servertransaction;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+// TODO(lifecycler): Add to presubmit after checking for flakiness.
+public class ClientTransactionTests {
+
+ @Test
+ public void testPrepare() {
+ ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+ ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+ ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+ ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class);
+ IBinder token = mock(IBinder.class);
+
+ ClientTransaction transaction = new ClientTransaction(null /* client */,
+ token /* activityToken */);
+ transaction.addCallback(callback1);
+ transaction.addCallback(callback2);
+ transaction.setLifecycleStateRequest(stateRequest);
+
+ transaction.prepare(clientTransactionHandler);
+
+ verify(callback1, times(1)).prepare(clientTransactionHandler, token);
+ verify(callback2, times(1)).prepare(clientTransactionHandler, token);
+ verify(stateRequest, times(1)).prepare(clientTransactionHandler, token);
+ }
+
+ @Test
+ public void testExecute() {
+ ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+ ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+ ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+ IBinder token = mock(IBinder.class);
+
+ ClientTransaction transaction = new ClientTransaction(null /* client */,
+ token /* activityToken */);
+ transaction.addCallback(callback1);
+ transaction.addCallback(callback2);
+ transaction.setLifecycleStateRequest(stateRequest);
+
+ ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class);
+ transaction.prepare(clientTransactionHandler);
+ transaction.execute(clientTransactionHandler);
+
+ verify(callback1, times(1)).execute(clientTransactionHandler, token);
+ verify(callback2, times(1)).execute(clientTransactionHandler, token);
+ verify(stateRequest, times(1)).execute(clientTransactionHandler, token);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
new file mode 100644
index 000000000000..9db7550a6099
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -0,0 +1,641 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.IApplicationThread;
+import android.app.IInstrumentationWatcher;
+import android.app.IUiAutomationConnection;
+import android.app.ProfilerInfo;
+import android.app.ResultInfo;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.ReferrerIntent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/** Test parcelling and unparcelling of transactions and transaction items. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+// TODO(lifecycler): Add to presubmit after checking for flakiness.
+public class TransactionParcelTests {
+
+ private Parcel mParcel;
+
+ @Before
+ public void setUp() throws Exception {
+ mParcel = Parcel.obtain();
+ }
+
+ @Test
+ public void testConfigurationChange() {
+ // Write to parcel
+ ConfigurationChangeItem item = new ConfigurationChangeItem(config());
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ ConfigurationChangeItem result = ConfigurationChangeItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testActivityConfigChange() {
+ // Write to parcel
+ ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem(config());
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ ActivityConfigurationChangeItem result =
+ ActivityConfigurationChangeItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testMoveToDisplay() {
+ // Write to parcel
+ MoveToDisplayItem item = new MoveToDisplayItem(4 /* targetDisplayId */, config());
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ MoveToDisplayItem result = MoveToDisplayItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testNewIntent() {
+ // Write to parcel
+ NewIntentItem item = new NewIntentItem(referrerIntentList(), true /* pause */);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ NewIntentItem result = NewIntentItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testActivityResult() {
+ // Write to parcel
+ ActivityResultItem item = new ActivityResultItem(resultInfoList());
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ ActivityResultItem result = ActivityResultItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testPipModeChange() {
+ // Write to parcel
+ PipModeChangeItem item = new PipModeChangeItem(true /* isInPipMode */, config());
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ PipModeChangeItem result = PipModeChangeItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testMultiWindowModeChange() {
+ // Write to parcel
+ MultiWindowModeChangeItem item = new MultiWindowModeChangeItem(
+ true /* isInMultiWindowMode */, config());
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ MultiWindowModeChangeItem result =
+ MultiWindowModeChangeItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testWindowVisibilityChange() {
+ // Write to parcel
+ WindowVisibilityItem item = new WindowVisibilityItem(true /* showWindow */);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ WindowVisibilityItem result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+
+ // Check different value
+ item = new WindowVisibilityItem(false);
+
+ mParcel = Parcel.obtain();
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ result = WindowVisibilityItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testDestroy() {
+ DestroyActivityItem item = new DestroyActivityItem(true /* finished */,
+ 135 /* configChanges */);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ DestroyActivityItem result = DestroyActivityItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testLaunch() {
+ // Write to parcel
+ Intent intent = new Intent("action");
+ int ident = 57;
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.flags = 42;
+ activityInfo.maxAspectRatio = 2.4f;
+ activityInfo.launchToken = "token";
+ activityInfo.applicationInfo = new ApplicationInfo();
+ activityInfo.packageName = "packageName";
+ activityInfo.name = "name";
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.assetsSeq = 5;
+ CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ String referrer = "referrer";
+ int procState = 4;
+ Bundle bundle = new Bundle();
+ bundle.putString("key", "value");
+ PersistableBundle persistableBundle = new PersistableBundle();
+ persistableBundle.putInt("k", 4);
+
+ LaunchActivityItem item = new LaunchActivityItem(intent, ident, activityInfo,
+ config(), overrideConfig, compat, referrer, null /* voiceInteractor */,
+ procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(),
+ true /* notResumed */, true /* isForward */, null /* profilerInfo */);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ LaunchActivityItem result = LaunchActivityItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testPause() {
+ // Write to parcel
+ PauseActivityItem item = new PauseActivityItem(true /* finished */, true /* userLeaving */,
+ 135 /* configChanges */, true /* dontReport */);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ PauseActivityItem result = PauseActivityItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testResume() {
+ // Write to parcel
+ ResumeActivityItem item = new ResumeActivityItem(27 /* procState */, true /* isForward */);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ ResumeActivityItem result = ResumeActivityItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testStop() {
+ // Write to parcel
+ StopActivityItem item = new StopActivityItem(true /* showWindow */, 14 /* configChanges */);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ StopActivityItem result = StopActivityItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
+ public void testClientTransaction() {
+ // Write to parcel
+ WindowVisibilityItem callback1 = new WindowVisibilityItem(true);
+ ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config());
+
+ StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */,
+ 78 /* configChanges */);
+
+ IApplicationThread appThread = new StubAppThread();
+ Binder activityToken = new Binder();
+
+ ClientTransaction transaction = new ClientTransaction(appThread, activityToken);
+ transaction.addCallback(callback1);
+ transaction.addCallback(callback2);
+ transaction.setLifecycleStateRequest(lifecycleRequest);
+
+ writeAndPrepareForReading(transaction);
+
+ // Read from parcel and assert
+ ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(transaction.hashCode(), result.hashCode());
+ assertTrue(transaction.equals(result));
+ }
+
+ @Test
+ public void testClientTransactionCallbacksOnly() {
+ // Write to parcel
+ WindowVisibilityItem callback1 = new WindowVisibilityItem(true);
+ ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(config());
+
+ IApplicationThread appThread = new StubAppThread();
+ Binder activityToken = new Binder();
+
+ ClientTransaction transaction = new ClientTransaction(appThread, activityToken);
+ transaction.addCallback(callback1);
+ transaction.addCallback(callback2);
+
+ writeAndPrepareForReading(transaction);
+
+ // Read from parcel and assert
+ ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(transaction.hashCode(), result.hashCode());
+ assertTrue(transaction.equals(result));
+ }
+
+ @Test
+ public void testClientTransactionLifecycleOnly() {
+ // Write to parcel
+ StopActivityItem lifecycleRequest = new StopActivityItem(true /* showWindow */,
+ 78 /* configChanges */);
+
+ IApplicationThread appThread = new StubAppThread();
+ Binder activityToken = new Binder();
+
+ ClientTransaction transaction = new ClientTransaction(appThread, activityToken);
+ transaction.setLifecycleStateRequest(lifecycleRequest);
+
+ writeAndPrepareForReading(transaction);
+
+ // Read from parcel and assert
+ ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(transaction.hashCode(), result.hashCode());
+ assertTrue(transaction.equals(result));
+ }
+
+ private static List<ResultInfo> resultInfoList() {
+ String resultWho1 = "resultWho1";
+ int requestCode1 = 7;
+ int resultCode1 = 4;
+ Intent data1 = new Intent("action1");
+ ResultInfo resultInfo1 = new ResultInfo(resultWho1, requestCode1, resultCode1, data1);
+
+ String resultWho2 = "resultWho2";
+ int requestCode2 = 8;
+ int resultCode2 = 6;
+ Intent data2 = new Intent("action2");
+ ResultInfo resultInfo2 = new ResultInfo(resultWho2, requestCode2, resultCode2, data2);
+
+ List<ResultInfo> resultInfoList = new ArrayList<>();
+ resultInfoList.add(resultInfo1);
+ resultInfoList.add(resultInfo2);
+
+ return resultInfoList;
+ }
+
+ private static List<ReferrerIntent> referrerIntentList() {
+ Intent intent1 = new Intent("action1");
+ ReferrerIntent referrerIntent1 = new ReferrerIntent(intent1, "referrer1");
+
+ Intent intent2 = new Intent("action2");
+ ReferrerIntent referrerIntent2 = new ReferrerIntent(intent2, "referrer2");
+
+ List<ReferrerIntent> referrerIntents = new ArrayList<>();
+ referrerIntents.add(referrerIntent1);
+ referrerIntents.add(referrerIntent2);
+
+ return referrerIntents;
+ }
+
+ private static Configuration config() {
+ Configuration config = new Configuration();
+ config.densityDpi = 10;
+ config.fontScale = 0.3f;
+ config.screenHeightDp = 15;
+ config.orientation = ORIENTATION_LANDSCAPE;
+ return config;
+ }
+
+ /** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */
+ private void writeAndPrepareForReading(Parcelable parcelable) {
+ parcelable.writeToParcel(mParcel, 0 /* flags */);
+ mParcel.setDataPosition(0);
+ }
+
+ /** Stub implementation of IApplicationThread that can be presented as {@link Binder}. */
+ class StubAppThread extends android.app.IApplicationThread.Stub {
+
+ @Override
+ public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleReceiver(Intent intent, ActivityInfo activityInfo,
+ CompatibilityInfo compatibilityInfo, int i, String s, Bundle bundle, boolean b,
+ int i1, int i2) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleCreateService(IBinder iBinder, ServiceInfo serviceInfo,
+ CompatibilityInfo compatibilityInfo, int i) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleStopService(IBinder iBinder) throws RemoteException {
+ }
+
+ @Override
+ public void bindApplication(String s, ApplicationInfo applicationInfo,
+ List<ProviderInfo> list, ComponentName componentName, ProfilerInfo profilerInfo,
+ Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher,
+ IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
+ boolean b2, boolean b3, Configuration configuration,
+ CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1)
+ throws RemoteException {
+ }
+
+ @Override
+ public void scheduleExit() throws RemoteException {
+ }
+
+ @Override
+ public void scheduleServiceArgs(IBinder iBinder, ParceledListSlice parceledListSlice)
+ throws RemoteException {
+ }
+
+ @Override
+ public void updateTimeZone() throws RemoteException {
+ }
+
+ @Override
+ public void processInBackground() throws RemoteException {
+ }
+
+ @Override
+ public void scheduleBindService(IBinder iBinder, Intent intent, boolean b, int i)
+ throws RemoteException {
+ }
+
+ @Override
+ public void scheduleUnbindService(IBinder iBinder, Intent intent) throws RemoteException {
+ }
+
+ @Override
+ public void dumpService(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder,
+ String[] strings) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleRegisteredReceiver(IIntentReceiver iIntentReceiver, Intent intent,
+ int i, String s, Bundle bundle, boolean b, boolean b1, int i1, int i2)
+ throws RemoteException {
+ }
+
+ @Override
+ public void scheduleLowMemory() throws RemoteException {
+ }
+
+ @Override
+ public void scheduleRelaunchActivity(IBinder iBinder, List<ResultInfo> list,
+ List<ReferrerIntent> list1, int i, boolean b, Configuration configuration,
+ Configuration configuration1, boolean b1) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleSleeping(IBinder iBinder, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void profilerControl(boolean b, ProfilerInfo profilerInfo, int i)
+ throws RemoteException {
+ }
+
+ @Override
+ public void setSchedulingGroup(int i) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo,
+ CompatibilityInfo compatibilityInfo, int i) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleDestroyBackupAgent(ApplicationInfo applicationInfo,
+ CompatibilityInfo compatibilityInfo) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleOnNewActivityOptions(IBinder iBinder, Bundle bundle)
+ throws RemoteException {
+ }
+
+ @Override
+ public void scheduleSuicide() throws RemoteException {
+ }
+
+ @Override
+ public void dispatchPackageBroadcast(int i, String[] strings) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleCrash(String s) throws RemoteException {
+ }
+
+ @Override
+ public void dumpActivity(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder,
+ String s, String[] strings) throws RemoteException {
+ }
+
+ @Override
+ public void clearDnsCache() throws RemoteException {
+ }
+
+ @Override
+ public void setHttpProxy(String s, String s1, String s2, Uri uri) throws RemoteException {
+ }
+
+ @Override
+ public void setCoreSettings(Bundle bundle) throws RemoteException {
+ }
+
+ @Override
+ public void updatePackageCompatibilityInfo(String s, CompatibilityInfo compatibilityInfo)
+ throws RemoteException {
+ }
+
+ @Override
+ public void scheduleTrimMemory(int i) throws RemoteException {
+ }
+
+ @Override
+ public void dumpMemInfo(ParcelFileDescriptor parcelFileDescriptor,
+ Debug.MemoryInfo memoryInfo, boolean b, boolean b1, boolean b2, boolean b3,
+ boolean b4, String[] strings) throws RemoteException {
+ }
+
+ @Override
+ public void dumpGfxInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings)
+ throws RemoteException {
+ }
+
+ @Override
+ public void dumpProvider(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder,
+ String[] strings) throws RemoteException {
+ }
+
+ @Override
+ public void dumpDbInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings)
+ throws RemoteException {
+ }
+
+ @Override
+ public void unstableProviderDied(IBinder iBinder) throws RemoteException {
+ }
+
+ @Override
+ public void requestAssistContextExtras(IBinder iBinder, IBinder iBinder1, int i, int i1,
+ int i2) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleTranslucentConversionComplete(IBinder iBinder, boolean b)
+ throws RemoteException {
+ }
+
+ @Override
+ public void setProcessState(int i) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleInstallProvider(ProviderInfo providerInfo) throws RemoteException {
+ }
+
+ @Override
+ public void updateTimePrefs(int i) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleEnterAnimationComplete(IBinder iBinder) throws RemoteException {
+ }
+
+ @Override
+ public void notifyCleartextNetwork(byte[] bytes) throws RemoteException {
+ }
+
+ @Override
+ public void startBinderTracking() throws RemoteException {
+ }
+
+ @Override
+ public void stopBinderTrackingAndDump(ParcelFileDescriptor parcelFileDescriptor)
+ throws RemoteException {
+ }
+
+ @Override
+ public void scheduleLocalVoiceInteractionStarted(IBinder iBinder,
+ IVoiceInteractor iVoiceInteractor) throws RemoteException {
+ }
+
+ @Override
+ public void handleTrustStorageUpdate() throws RemoteException {
+ }
+
+ @Override
+ public void attachAgent(String s) throws RemoteException {
+ }
+
+ @Override
+ public void scheduleApplicationInfoChanged(ApplicationInfo applicationInfo)
+ throws RemoteException {
+ }
+
+ @Override
+ public void setNetworkBlockSeq(long l) throws RemoteException {
+ }
+
+ @Override
+ public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path,
+ ParcelFileDescriptor fd) {
+ }
+
+ @Override
+ public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 492ef7288fcc..454d2096eca8 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -127,6 +127,7 @@ public class SettingsBackupTest {
Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_SAP_PRIORITY_PREFIX,
+ Settings.Global.BLUETOOTH_HEARING_AID_PRIORITY_PREFIX,
Settings.Global.BOOT_COUNT,
Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL,
Settings.Global.CAPTIVE_PORTAL_HTTPS_URL,
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
new file mode 100644
index 000000000000..6dd787d2e038
--- /dev/null
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -0,0 +1,347 @@
+/*
+ * 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.view;
+
+import static android.view.DisplayCutout.NO_CUTOUT;
+
+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.graphics.Point;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.DisplayCutout.ParcelableWrapper;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayCutoutTest {
+
+ /** This is not a consistent cutout. Useful for verifying insets in one go though. */
+ final DisplayCutout mCutoutNumbers = new DisplayCutout(
+ new Rect(1, 2, 3, 4),
+ new Rect(5, 6, 7, 8),
+ Arrays.asList(
+ new Point(9, 10),
+ new Point(11, 12),
+ new Point(13, 14),
+ new Point(15, 16)));
+
+ final DisplayCutout mCutoutTop = createCutoutTop();
+
+ @Test
+ public void hasCutout() throws Exception {
+ assertFalse(NO_CUTOUT.hasCutout());
+ assertTrue(mCutoutTop.hasCutout());
+ }
+
+ @Test
+ public void getSafeInsets() throws Exception {
+ assertEquals(1, mCutoutNumbers.getSafeInsetLeft());
+ assertEquals(2, mCutoutNumbers.getSafeInsetTop());
+ assertEquals(3, mCutoutNumbers.getSafeInsetRight());
+ assertEquals(4, mCutoutNumbers.getSafeInsetBottom());
+
+ Rect safeInsets = new Rect();
+ mCutoutNumbers.getSafeInsets(safeInsets);
+
+ assertEquals(new Rect(1, 2, 3, 4), safeInsets);
+ }
+
+ @Test
+ public void getBoundingRect() throws Exception {
+ Rect boundingRect = new Rect();
+ mCutoutTop.getBoundingRect(boundingRect);
+
+ assertEquals(new Rect(50, 0, 75, 100), boundingRect);
+ }
+
+ @Test
+ public void getBoundingPolygon() throws Exception {
+ ArrayList<Point> boundingPolygon = new ArrayList<>();
+ mCutoutTop.getBoundingPolygon(boundingPolygon);
+
+ assertEquals(Arrays.asList(
+ new Point(75, 0),
+ new Point(50, 0),
+ new Point(75, 100),
+ new Point(50, 100)), boundingPolygon);
+ }
+
+ @Test
+ public void testHashCode() throws Exception {
+ assertEquals(mCutoutTop.hashCode(), createCutoutTop().hashCode());
+ assertNotEquals(mCutoutTop.hashCode(), mCutoutNumbers.hashCode());
+ }
+
+ @Test
+ public void testEquals() throws Exception {
+ assertEquals(mCutoutTop, createCutoutTop());
+ assertNotEquals(mCutoutTop, mCutoutNumbers);
+ }
+
+ @Test
+ public void testToString() throws Exception {
+ assertFalse(mCutoutTop.toString().isEmpty());
+ assertFalse(mCutoutNumbers.toString().isEmpty());
+ }
+
+ @Test
+ public void inset_immutable() throws Exception {
+ DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
+
+ assertEquals("original instance must not be mutated", createCutoutTop(), mCutoutTop);
+ }
+
+ @Test
+ public void inset_insets_withLeftCutout() throws Exception {
+ DisplayCutout cutout = createCutoutWithInsets(100, 0, 0, 0).inset(1, 2, 3, 4);
+
+ assertEquals(cutout.getSafeInsetLeft(), 99);
+ assertEquals(cutout.getSafeInsetTop(), 0);
+ assertEquals(cutout.getSafeInsetRight(), 0);
+ assertEquals(cutout.getSafeInsetBottom(), 0);
+ }
+
+ @Test
+ public void inset_insets_withTopCutout() throws Exception {
+ DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
+
+ assertEquals(cutout.getSafeInsetLeft(), 0);
+ assertEquals(cutout.getSafeInsetTop(), 98);
+ assertEquals(cutout.getSafeInsetRight(), 0);
+ assertEquals(cutout.getSafeInsetBottom(), 0);
+ }
+
+ @Test
+ public void inset_insets_withRightCutout() throws Exception {
+ DisplayCutout cutout = createCutoutWithInsets(0, 0, 100, 0).inset(1, 2, 3, 4);
+
+ assertEquals(cutout.getSafeInsetLeft(), 0);
+ assertEquals(cutout.getSafeInsetTop(), 0);
+ assertEquals(cutout.getSafeInsetRight(), 97);
+ assertEquals(cutout.getSafeInsetBottom(), 0);
+ }
+
+ @Test
+ public void inset_insets_withBottomCutout() throws Exception {
+ DisplayCutout cutout = createCutoutWithInsets(0, 0, 0, 100).inset(1, 2, 3, 4);
+
+ assertEquals(cutout.getSafeInsetLeft(), 0);
+ assertEquals(cutout.getSafeInsetTop(), 0);
+ assertEquals(cutout.getSafeInsetRight(), 0);
+ assertEquals(cutout.getSafeInsetBottom(), 96);
+ }
+
+ @Test
+ public void inset_insets_consumeInset() throws Exception {
+ DisplayCutout cutout = mCutoutTop.inset(0, 1000, 0, 0);
+
+ assertEquals(cutout.getSafeInsetLeft(), 0);
+ assertEquals(cutout.getSafeInsetTop(), 0);
+ assertEquals(cutout.getSafeInsetRight(), 0);
+ assertEquals(cutout.getSafeInsetBottom(), 0);
+
+ assertFalse(cutout.hasCutout());
+ }
+
+ @Test
+ public void inset_bounds() throws Exception {
+ DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
+
+ Rect boundingRect = new Rect();
+ cutout.getBoundingRect(boundingRect);
+
+ assertEquals(new Rect(49, -2, 74, 98), boundingRect);
+
+ ArrayList<Point> boundingPolygon = new ArrayList<>();
+ cutout.getBoundingPolygon(boundingPolygon);
+
+ assertEquals(Arrays.asList(
+ new Point(74, -2),
+ new Point(49, -2),
+ new Point(74, 98),
+ new Point(49, 98)), boundingPolygon);
+ }
+
+ @Test
+ public void calculateRelativeTo_top() throws Exception {
+ DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(0, 0, 200, 400));
+
+ Rect insets = new Rect();
+ cutout.getSafeInsets(insets);
+
+ assertEquals(new Rect(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void calculateRelativeTo_left() throws Exception {
+ DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(0, 0, 400, 200));
+
+ Rect insets = new Rect();
+ cutout.getSafeInsets(insets);
+
+ assertEquals(new Rect(75, 0, 0, 0), insets);
+ }
+
+ @Test
+ public void calculateRelativeTo_bottom() throws Exception {
+ DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(0, -300, 200, 100));
+
+ Rect insets = new Rect();
+ cutout.getSafeInsets(insets);
+
+ assertEquals(new Rect(0, 0, 0, 100), insets);
+ }
+
+ @Test
+ public void calculateRelativeTo_right() throws Exception {
+ DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(-400, -200, 100, 100));
+
+ Rect insets = new Rect();
+ cutout.getSafeInsets(insets);
+
+ assertEquals(new Rect(0, 0, 50, 0), insets);
+ }
+
+ @Test
+ public void calculateRelativeTo_bounds() throws Exception {
+ DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(-1000, -2000, 100, 200));
+
+
+ Rect boundingRect = new Rect();
+ cutout.getBoundingRect(boundingRect);
+ assertEquals(new Rect(1050, 2000, 1075, 2100), boundingRect);
+
+ ArrayList<Point> boundingPolygon = new ArrayList<>();
+ cutout.getBoundingPolygon(boundingPolygon);
+
+ assertEquals(Arrays.asList(
+ new Point(1075, 2000),
+ new Point(1050, 2000),
+ new Point(1075, 2100),
+ new Point(1050, 2100)), boundingPolygon);
+ }
+
+ @Test
+ public void fromBoundingPolygon() throws Exception {
+ assertEquals(
+ new DisplayCutout(
+ new Rect(0, 0, 0, 0), // fromBoundingPolygon won't calculate safe insets.
+ new Rect(50, 0, 75, 100),
+ Arrays.asList(
+ new Point(75, 0),
+ new Point(50, 0),
+ new Point(75, 100),
+ new Point(50, 100))),
+ DisplayCutout.fromBoundingPolygon(
+ Arrays.asList(
+ new Point(75, 0),
+ new Point(50, 0),
+ new Point(75, 100),
+ new Point(50, 100))));
+ }
+
+ @Test
+ public void parcel_unparcel_regular() {
+ Parcel p = Parcel.obtain();
+
+ new ParcelableWrapper(mCutoutTop).writeToParcel(p, 0);
+ int posAfterWrite = p.dataPosition();
+
+ p.setDataPosition(0);
+
+ assertEquals(mCutoutTop, ParcelableWrapper.CREATOR.createFromParcel(p).get());
+ assertEquals(posAfterWrite, p.dataPosition());
+ }
+
+ @Test
+ public void parcel_unparcel_nocutout() {
+ Parcel p = Parcel.obtain();
+
+ new ParcelableWrapper(NO_CUTOUT).writeToParcel(p, 0);
+ int posAfterWrite = p.dataPosition();
+
+ p.setDataPosition(0);
+
+ assertEquals(NO_CUTOUT, ParcelableWrapper.CREATOR.createFromParcel(p).get());
+ assertEquals(posAfterWrite, p.dataPosition());
+ }
+
+ @Test
+ public void parcel_unparcel_inplace() {
+ Parcel p = Parcel.obtain();
+
+ new ParcelableWrapper(mCutoutTop).writeToParcel(p, 0);
+ int posAfterWrite = p.dataPosition();
+
+ p.setDataPosition(0);
+
+ ParcelableWrapper wrapper = new ParcelableWrapper();
+ wrapper.readFromParcel(p);
+
+ assertEquals(mCutoutTop, wrapper.get());
+ assertEquals(posAfterWrite, p.dataPosition());
+ }
+
+ @Test
+ public void wrapper_hashcode() throws Exception {
+ assertEquals(new ParcelableWrapper(mCutoutTop).hashCode(),
+ new ParcelableWrapper(createCutoutTop()).hashCode());
+ assertNotEquals(new ParcelableWrapper(mCutoutTop).hashCode(),
+ new ParcelableWrapper(mCutoutNumbers).hashCode());
+ }
+
+ @Test
+ public void wrapper_equals() throws Exception {
+ assertEquals(new ParcelableWrapper(mCutoutTop), new ParcelableWrapper(createCutoutTop()));
+ assertNotEquals(new ParcelableWrapper(mCutoutTop), new ParcelableWrapper(mCutoutNumbers));
+ }
+
+ private static DisplayCutout createCutoutTop() {
+ return new DisplayCutout(
+ new Rect(0, 100, 0, 0),
+ new Rect(50, 0, 75, 100),
+ Arrays.asList(
+ new Point(75, 0),
+ new Point(50, 0),
+ new Point(75, 100),
+ new Point(50, 100)));
+ }
+
+ private static DisplayCutout createCutoutWithInsets(int left, int top, int right, int bottom) {
+ return new DisplayCutout(
+ new Rect(left, top, right, bottom),
+ new Rect(50, 0, 75, 100),
+ Arrays.asList(
+ new Point(75, 0),
+ new Point(50, 0),
+ new Point(75, 100),
+ new Point(50, 100)));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index c8dc9ff51a1b..2f32d13693c7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,26 +14,23 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package android.view.accessibility;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.support.test.runner.AndroidJUnit4;
-import android.view.accessibility.AccessibilityCache;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityInteractionClient;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityWindowInfo;
import android.view.View;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -48,27 +45,27 @@ import java.util.concurrent.atomic.AtomicInteger;
@RunWith(AndroidJUnit4.class)
public class AccessibilityCacheTest {
- private static int WINDOW_ID_1 = 0xBEEF;
- private static int WINDOW_ID_2 = 0xFACE;
- private static int SINGLE_VIEW_ID = 0xCAFE;
- private static int OTHER_VIEW_ID = 0xCAB2;
- private static int PARENT_VIEW_ID = 0xFED4;
- private static int CHILD_VIEW_ID = 0xFEED;
- private static int OTHER_CHILD_VIEW_ID = 0xACE2;
- private static int MOCK_CONNECTION_ID = 1;
+ private static final int WINDOW_ID_1 = 0xBEEF;
+ private static final int WINDOW_ID_2 = 0xFACE;
+ private static final int SINGLE_VIEW_ID = 0xCAFE;
+ private static final int OTHER_VIEW_ID = 0xCAB2;
+ private static final int PARENT_VIEW_ID = 0xFED4;
+ private static final int CHILD_VIEW_ID = 0xFEED;
+ private static final int OTHER_CHILD_VIEW_ID = 0xACE2;
+ private static final int MOCK_CONNECTION_ID = 1;
AccessibilityCache mAccessibilityCache;
AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
- AtomicInteger numA11yNodeInfosInUse = new AtomicInteger(0);
- AtomicInteger numA11yWinInfosInUse = new AtomicInteger(0);
+ AtomicInteger mNumA11yNodeInfosInUse = new AtomicInteger(0);
+ AtomicInteger mNumA11yWinInfosInUse = new AtomicInteger(0);
@Before
public void setUp() {
mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class);
when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true);
mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
- AccessibilityNodeInfo.setNumInstancesInUseCounter(numA11yNodeInfosInUse);
- AccessibilityWindowInfo.setNumInstancesInUseCounter(numA11yWinInfosInUse);
+ AccessibilityNodeInfo.setNumInstancesInUseCounter(mNumA11yNodeInfosInUse);
+ AccessibilityWindowInfo.setNumInstancesInUseCounter(mNumA11yWinInfosInUse);
}
@After
@@ -76,7 +73,8 @@ public class AccessibilityCacheTest {
// Make sure we're recycling all of our window and node infos
mAccessibilityCache.clear();
AccessibilityInteractionClient.getInstance().clearCache();
- assertEquals(0, numA11yWinInfosInUse.get());
+ assertEquals(0, mNumA11yWinInfosInUse.get());
+ assertEquals(0, mNumA11yNodeInfosInUse.get());
}
@Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
new file mode 100644
index 000000000000..8a5fc2d11195
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.view.accessibility;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for AccessibilityInteractionClient
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityInteractionClientTest {
+ private static final int MOCK_CONNECTION_ID = 0xabcd;
+
+ private MockConnection mMockConnection = new MockConnection();
+ @Mock private AccessibilityCache mMockCache;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ AccessibilityInteractionClient.setCache(mMockCache);
+ AccessibilityInteractionClient.addConnection(MOCK_CONNECTION_ID, mMockConnection);
+ }
+
+ /**
+ * When the AccessibilityCache refreshes the nodes it contains, it gets very confused if
+ * it is called to update itself during the refresh. It tries to update the node that it's
+ * in the process of refreshing, which leads to AccessibilityNodeInfos in inconsistent states.
+ */
+ @Test
+ public void findA11yNodeInfoByA11yId_whenBypassingCache_doesntTouchCache() {
+ final int windowId = 0x1234;
+ final long accessibilityNodeId = 0x4321L;
+ AccessibilityNodeInfo nodeFromConnection = AccessibilityNodeInfo.obtain();
+ nodeFromConnection.setSourceNodeId(accessibilityNodeId, windowId);
+ mMockConnection.mInfosToReturn = Arrays.asList(nodeFromConnection);
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ AccessibilityNodeInfo node = client.findAccessibilityNodeInfoByAccessibilityId(
+ MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
+ assertEquals("Node got lost along the way", nodeFromConnection, node);
+
+ verifyZeroInteractions(mMockCache);
+ }
+
+ private static class MockConnection extends AccessibilityServiceConnectionImpl {
+ List<AccessibilityNodeInfo> mInfosToReturn;
+
+ @Override
+ public boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ long accessibilityNodeId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
+ Bundle arguments) {
+ try {
+ callback.setFindAccessibilityNodeInfosResult(mInfosToReturn, interactionId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ return true;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 7f979738ee52..79e6316ed614 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -1,10 +1,25 @@
-package com.android.server.accessibility;
+/*
+ * 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.view.accessibility;
import static org.junit.Assert.fail;
import android.support.test.runner.AndroidJUnit4;
import android.util.ArraySet;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.util.CollectionUtils;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
new file mode 100644
index 000000000000..d3bbee71d564
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -0,0 +1,133 @@
+/*
+ * 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.view.accessibility;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.content.pm.ParceledListSlice;
+import android.graphics.Region;
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement
+ * all of the methods
+ */
+public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceConnection.Stub {
+ public void setServiceInfo(AccessibilityServiceInfo info) {}
+
+ public boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ long accessibilityNodeId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
+ Bundle arguments) {
+ return false;
+ }
+
+ public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId,
+ long accessibilityNodeId, String text, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long threadId) {
+ return false;
+ }
+
+ public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
+ long accessibilityNodeId, String viewId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long threadId) {
+ return false;
+ }
+
+ public boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback,
+ long threadId) {
+ return false;
+ }
+
+ public boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback,
+ long threadId) {
+ return false;
+ }
+
+ public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
+ int action, Bundle arguments, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long threadId) {
+ return false;
+ }
+
+ public AccessibilityWindowInfo getWindow(int windowId) {
+ return null;
+ }
+
+ public List<AccessibilityWindowInfo> getWindows() {
+ return null;
+ }
+
+ public AccessibilityServiceInfo getServiceInfo() {
+ return null;
+ }
+
+ public boolean performGlobalAction(int action) {
+ return false;
+ }
+
+ public void disableSelf() {}
+
+ public void setOnKeyEventResult(boolean handled, int sequence) {}
+
+ public float getMagnificationScale() {
+ return 0.0f;
+ }
+
+ public float getMagnificationCenterX() {
+ return 0.0f;
+ }
+
+ public float getMagnificationCenterY() {
+ return 0.0f;
+ }
+
+ public Region getMagnificationRegion() {
+ return null;
+ }
+
+ public boolean resetMagnification(boolean animate) {
+ return false;
+ }
+
+ public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
+ boolean animate) {
+ return false;
+ }
+
+ public void setMagnificationCallbackEnabled(boolean enabled) {}
+
+ public boolean setSoftKeyboardShowMode(int showMode) {
+ return false;
+ }
+
+ public void setSoftKeyboardCallbackEnabled(boolean enabled) {}
+
+ public boolean isAccessibilityButtonAvailable() {
+ return false;
+ }
+
+ public void sendGesture(int sequence, ParceledListSlice gestureSteps) {}
+
+ public boolean isFingerprintGestureDetectionAvailable() {
+ return false;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
new file mode 100644
index 000000000000..682a0021b274
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -0,0 +1,399 @@
+/*
+ * 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.internal.os;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.frameworks.coretests.aidl.ICmdCallback;
+import com.android.frameworks.coretests.aidl.ICmdReceiver;
+
+import android.app.KeyguardManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BstatsCpuTimesValidationTest {
+ private static final String TAG = BstatsCpuTimesValidationTest.class.getName();
+
+ private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
+ private static final String TEST_ACTIVITY = TEST_PKG + ".TestActivity";
+ private static final String ISOLATED_TEST_SERVICE = TEST_PKG + ".IsolatedTestService";
+
+ private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver";
+
+ private static final int BATTERY_STATE_TIMEOUT_MS = 2000;
+ private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 200;
+
+ private static final int START_ACTIVITY_TIMEOUT_MS = 2000;
+ private static final int START_ISOLATED_SERVICE_TIMEOUT_MS = 2000;
+
+ private static final int GENERAL_TIMEOUT_MS = 1000;
+ private static final int GENERAL_INTERVAL_MS = 100;
+
+ private static final int WORK_DURATION_MS = 2000;
+
+ private static Context sContext;
+ private static UiDevice sUiDevice;
+ private static int sTestPkgUid;
+ private static boolean sCpuFreqTimesAvailable;
+
+ @BeforeClass
+ public static void setupOnce() throws Exception {
+ sContext = InstrumentationRegistry.getContext();
+ sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+ sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_PKG, 0);
+ sCpuFreqTimesAvailable = cpuFreqTimesAvailable();
+ }
+
+ // Checks cpu freq times of system uid as an indication of whether /proc/uid_time_in_state
+ // kernel node is available.
+ private static boolean cpuFreqTimesAvailable() throws Exception {
+ final long[] cpuTimes = getAllCpuFreqTimes(Process.SYSTEM_UID);
+ return cpuTimes != null;
+ }
+
+ @Test
+ public void testCpuFreqTimes() throws Exception {
+ assumeTrue(sCpuFreqTimesAvailable);
+
+ batteryOnScreenOn();
+ forceStop();
+ resetBatteryStats();
+ final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
+ assertNull("Initial snapshot should be null", initialSnapshot);
+ doSomeWork();
+ forceStop();
+
+ final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid);
+ assertCpuTimesValid(cpuTimesMs);
+ long actualCpuTimeMs = 0;
+ for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
+ actualCpuTimeMs += cpuTimesMs[i];
+ }
+ assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs);
+ batteryOffScreenOn();
+ }
+
+ @Test
+ public void testCpuFreqTimes_screenOff() throws Exception {
+ assumeTrue(sCpuFreqTimesAvailable);
+
+ batteryOnScreenOff();
+ forceStop();
+ resetBatteryStats();
+ final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
+ assertNull("Initial snapshot should be null", initialSnapshot);
+ doSomeWork();
+ forceStop();
+
+ final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid);
+ assertCpuTimesValid(cpuTimesMs);
+ long actualTotalCpuTimeMs = 0;
+ for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
+ actualTotalCpuTimeMs += cpuTimesMs[i];
+ }
+ assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualTotalCpuTimeMs);
+ long actualScreenOffCpuTimeMs = 0;
+ for (int i = cpuTimesMs.length / 2; i < cpuTimesMs.length; ++i) {
+ actualScreenOffCpuTimeMs += cpuTimesMs[i];
+ }
+ assertApproximateValue("Incorrect screen-off cpu time",
+ WORK_DURATION_MS, actualScreenOffCpuTimeMs);
+ batteryOffScreenOn();
+ }
+
+ @Test
+ public void testCpuFreqTimes_isolatedProcess() throws Exception {
+ assumeTrue(sCpuFreqTimesAvailable);
+
+ batteryOnScreenOn();
+ forceStop();
+ resetBatteryStats();
+ final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
+ assertNull("Initial snapshot should be null", initialSnapshot);
+ doSomeWorkInIsolatedProcess();
+ forceStop();
+
+ final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid);
+ assertCpuTimesValid(cpuTimesMs);
+ long actualCpuTimeMs = 0;
+ for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
+ actualCpuTimeMs += cpuTimesMs[i];
+ }
+ assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs);
+ batteryOffScreenOn();
+ }
+
+ private void assertCpuTimesValid(long[] cpuTimes) {
+ assertNotNull(cpuTimes);
+ for (int i = 0; i < cpuTimes.length; ++i) {
+ if (cpuTimes[i] < 0) {
+ fail("Malformed cpu times data (-ve values): " + Arrays.toString(cpuTimes));
+ }
+ }
+ final int numFreqs = cpuTimes.length / 2;
+ for (int i = 0; i < numFreqs; ++i) {
+ if (cpuTimes[i] < cpuTimes[numFreqs + i]) {
+ fail("Malformed cpu times data (screen-off > total)" + Arrays.toString(cpuTimes));
+ }
+ }
+ }
+
+ private void assertApproximateValue(String errorPrefix, long expectedValue, long actualValue) {
+ assertValueRange(errorPrefix, actualValue, expectedValue * 0.5, expectedValue * 1.5);
+ }
+
+ private void assertValueRange(String errorPrefix,
+ long actualvalue, double minValue, double maxValue) {
+ final String errorMsg = String.format(errorPrefix + "; actual=%s; min=%s; max=%s",
+ actualvalue, minValue, maxValue);
+ assertTrue(errorMsg, actualvalue < maxValue);
+ assertTrue(errorMsg, actualvalue > minValue);
+ }
+
+ private void doSomeWork() throws Exception {
+ final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startActivity());
+ receiver.doSomeWork(WORK_DURATION_MS);
+ receiver.finishHost();
+ }
+
+ private void doSomeWorkInIsolatedProcess() throws Exception {
+ final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startIsolatedService());
+ receiver.doSomeWork(WORK_DURATION_MS);
+ receiver.finishHost();
+ }
+
+ private IBinder startIsolatedService() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final IBinder[] binders = new IBinder[1];
+ final ServiceConnection connection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ binders[0] = service;
+ latch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+ final Intent launchIntent = new Intent()
+ .setComponent(new ComponentName(TEST_PKG, ISOLATED_TEST_SERVICE));
+ sContext.bindService(launchIntent, connection, Context.BIND_AUTO_CREATE
+ | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_FOREGROUND);
+ if (latch.await(START_ISOLATED_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ if (binders[0] == null) {
+ fail("Receiver binder should not be null");
+ }
+ return binders[0];
+ } else {
+ fail("Timed out waiting for the isolated test service to start");
+ }
+ return null;
+ }
+
+ private IBinder startActivity() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Intent launchIntent = new Intent()
+ .setComponent(new ComponentName(TEST_PKG, TEST_ACTIVITY));
+ final Bundle extras = new Bundle();
+ final IBinder[] binders = new IBinder[1];
+ extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() {
+ @Override
+ public void onActivityLaunched(IBinder receiver) {
+ binders[0] = receiver;
+ latch.countDown();
+ }
+ });
+ launchIntent.putExtras(extras);
+ sContext.startActivity(launchIntent);
+ if (latch.await(START_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ if (binders[0] == null) {
+ fail("Receiver binder should not be null");
+ }
+ return binders[0];
+ } else {
+ fail("Timed out waiting for the test activity to start; testUid=" + sTestPkgUid);
+ }
+ return null;
+ }
+
+ private static long[] getAllCpuFreqTimes(int uid) throws Exception {
+ final String checkinDump = executeCmdSilent("dumpsys batterystats --checkin");
+ final Pattern pattern = Pattern.compile(uid + ",l,ctf,A,(.*?)\n");
+ final Matcher matcher = pattern.matcher(checkinDump);
+ if (!matcher.find()) {
+ return null;
+ }
+ final String[] uidTimesStr = matcher.group(1).split(",");
+ final int freqCount = Integer.parseInt(uidTimesStr[0]);
+ if (uidTimesStr.length != (2 * freqCount + 1)) {
+ fail("Malformed data: " + Arrays.toString(uidTimesStr));
+ }
+ final long[] cpuTimes = new long[freqCount * 2];
+ for (int i = 0; i < cpuTimes.length; ++i) {
+ cpuTimes[i] = Long.parseLong(uidTimesStr[i + 1]);
+ }
+ return cpuTimes;
+ }
+
+ private void resetBatteryStats() throws Exception {
+ executeCmd("dumpsys batterystats --reset");
+ }
+
+ private void batteryOnScreenOn() throws Exception {
+ batteryOn();
+ screenOn();
+ }
+
+ private void batteryOnScreenOff() throws Exception {
+ batteryOn();
+ screenoff();
+ }
+
+ private void batteryOffScreenOn() throws Exception {
+ batteryOff();
+ screenOn();
+ }
+
+ private void batteryOn() throws Exception {
+ executeCmd("dumpsys battery unplug");
+ assertBatteryState(false);
+ }
+
+ private void batteryOff() throws Exception {
+ executeCmd("dumpsys battery reset");
+ assertBatteryState(true);
+ }
+
+ private void screenOn() throws Exception {
+ executeCmd("input keyevent KEYCODE_WAKEUP");
+ executeCmd("wm dismiss-keyguard");
+ assertKeyguardUnLocked();
+ assertScreenInteractive(true);
+ }
+
+ private void screenoff() throws Exception {
+ executeCmd("input keyevent KEYCODE_SLEEP");
+ assertScreenInteractive(false);
+ }
+
+ private void forceStop() throws Exception {
+ executeCmd("cmd activity force-stop " + TEST_PKG);
+ assertUidState(PROCESS_STATE_NONEXISTENT);
+ }
+
+ private void assertUidState(int state) throws Exception {
+ final String uidStateStr = executeCmd("cmd activity get-uid-state " + sTestPkgUid);
+ final int uidState = Integer.parseInt(uidStateStr.split(" ")[0]);
+ assertEquals(state, uidState);
+ }
+
+ private void assertKeyguardUnLocked() {
+ final KeyguardManager keyguardManager =
+ (KeyguardManager) sContext.getSystemService(Context.KEYGUARD_SERVICE);
+ assertDelayedCondition("Keyguard should be unlocked",
+ () -> !keyguardManager.isKeyguardLocked());
+ }
+
+ private void assertScreenInteractive(boolean interactive) {
+ final PowerManager powerManager =
+ (PowerManager) sContext.getSystemService(Context.POWER_SERVICE);
+ assertDelayedCondition("Unexpected screen interactive state",
+ () -> interactive == powerManager.isInteractive());
+ }
+
+ private void assertDelayedCondition(String errorMsg, ExpectedCondition condition) {
+ final long endTime = SystemClock.uptimeMillis() + GENERAL_TIMEOUT_MS;
+ while (SystemClock.uptimeMillis() <= endTime) {
+ if (condition.isTrue()) {
+ return;
+ }
+ SystemClock.sleep(GENERAL_INTERVAL_MS);
+ }
+ if (!condition.isTrue()) {
+ fail(errorMsg);
+ }
+ }
+
+ private void assertBatteryState(boolean pluggedIn) throws Exception {
+ final long endTime = SystemClock.uptimeMillis() + BATTERY_STATE_TIMEOUT_MS;
+ while (isDevicePluggedIn() != pluggedIn && SystemClock.uptimeMillis() <= endTime) {
+ Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS);
+ }
+ if (isDevicePluggedIn() != pluggedIn) {
+ fail("Timed out waiting for the plugged-in state to change,"
+ + " expected pluggedIn: " + pluggedIn);
+ }
+ }
+
+ private boolean isDevicePluggedIn() {
+ final Intent batteryIntent = sContext.registerReceiver(null,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0;
+ }
+
+ private String executeCmd(String cmd) throws Exception {
+ final String result = sUiDevice.executeShellCommand(cmd).trim();
+ Log.d(TAG, String.format("Result for '%s': %s", cmd, result));
+ return result;
+ }
+
+ private static String executeCmdSilent(String cmd) throws Exception {
+ return sUiDevice.executeShellCommand(cmd).trim();
+ }
+
+ private interface ExpectedCondition {
+ boolean isTrue();
+ }
+}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 3fe730fdacba..2daf733d057f 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -40,6 +40,7 @@ import android.security.keystore.KeyProperties;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
+import java.security.KeyPair;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
@@ -418,6 +419,18 @@ public final class KeyChain {
@Nullable @WorkerThread
public static PrivateKey getPrivateKey(@NonNull Context context, @NonNull String alias)
throws KeyChainException, InterruptedException {
+ KeyPair keyPair = getKeyPair(context, alias);
+ if (keyPair != null) {
+ return keyPair.getPrivate();
+ }
+
+ return null;
+ }
+
+ /** @hide */
+ @Nullable @WorkerThread
+ public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias)
+ throws KeyChainException, InterruptedException {
if (alias == null) {
throw new NullPointerException("alias == null");
}
@@ -439,7 +452,7 @@ public final class KeyChain {
return null;
} else {
try {
- return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
} catch (RuntimeException | UnrecoverableKeyException e) {
throw new KeyChainException(e);
diff --git a/location/tests/Android.mk b/location/tests/Android.mk
new file mode 100644
index 000000000000..57848f353fd4
--- /dev/null
+++ b/location/tests/Android.mk
@@ -0,0 +1,3 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under, $(LOCAL_PATH)) \ No newline at end of file
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 6c87a9d8d915..143182f83ace 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1586,8 +1586,10 @@ public final class TvInputManager {
* @param info The TV input which will use the acquired Hardware.
* @return Hardware on success, {@code null} otherwise.
*
+ * @hide
* @removed
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback,
TvInputInfo info) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index ba1c9e3cc43b..52b4f4df5735 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -801,6 +801,9 @@ class SettingsProtoDumpUtil {
Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX,
GlobalSettingsProto.BLUETOOTH_PAN_PRIORITY_PREFIX);
dumpSetting(s, p,
+ Settings.Global.BLUETOOTH_HEARING_AID_PRIORITY_PREFIX,
+ GlobalSettingsProto.BLUETOOTH_HEARING_AID_PRIORITY_PREFIX);
+ dumpSetting(s, p,
Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
GlobalSettingsProto.ACTIVITY_MANAGER_CONSTANTS);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index cf7923805f0f..8e065d1bf06b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -250,10 +250,7 @@
<string name="doze_brightness_sensor_type" translatable="false"></string>
<!-- Doze: pulse parameter - how long does it take to fade in? -->
- <integer name="doze_pulse_duration_in">900</integer>
-
- <!-- Doze: pulse parameter - how long does it take to fade in after a pickup? -->
- <integer name="doze_pulse_duration_in_pickup">130</integer>
+ <integer name="doze_pulse_duration_in">130</integer>
<!-- Doze: pulse parameter - once faded in, how long does it stay visible? -->
<integer name="doze_pulse_duration_visible">6000</integer>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index cfd95b41825a..884e18918fd6 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -47,6 +47,7 @@
<item type="id" name="qs_icon_tag"/>
<item type="id" name="qs_slash_tag"/>
<item type="id" name="scrim"/>
+ <item type="id" name="scrim_blanking"/>
<item type="id" name="scrim_target"/>
<item type="id" name="scrim_alpha_start"/>
<item type="id" name="scrim_alpha_end"/>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 0c067ff38295..526a8f464441 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -28,6 +28,7 @@ import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockIcon;
@@ -86,10 +87,10 @@ public class SystemUIFactory {
public ScrimController createScrimController(LightBarController lightBarController,
ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
- LockscreenWallpaper lockscreenWallpaper,
- Consumer<Boolean> scrimVisibleListener) {
+ LockscreenWallpaper lockscreenWallpaper, Consumer<Boolean> scrimVisibleListener,
+ DozeParameters dozeParameters) {
return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
- scrimVisibleListener);
+ scrimVisibleListener, dozeParameters);
}
public NotificationIconAreaController createNotificationIconAreaController(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 7db118d7fb1c..2f607eee4f16 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -35,7 +35,6 @@ public interface DozeHost {
boolean isBlockingDoze();
void startPendingIntentDismissingKeyguard(PendingIntent intent);
- void abortPulsing();
void extendPulse();
void setAnimateWakeup(boolean animateWakeup);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b99e76a28c6d..c92acd068745 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2028,12 +2028,9 @@ public class KeyguardViewMediator extends SystemUI {
}
public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
- ViewGroup container,
- ScrimController scrimController,
- FingerprintUnlockController fingerprintUnlockController) {
+ ViewGroup container, FingerprintUnlockController fingerprintUnlockController) {
mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container,
- scrimController, fingerprintUnlockController,
- mDismissCallbackRegistry);
+ fingerprintUnlockController, mDismissCallbackRegistry);
return mStatusBarKeyguardViewManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 991c3c83cbc1..e9096447bbf6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -162,7 +162,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
Matrix matrix = new Matrix();
int overlayColor = 0x40FFFFFF;
- Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, data.image.getConfig());
+ Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
matrix.setTranslate((previewWidth - mImageWidth) / 2, (previewHeight - mImageHeight) / 2);
c.setBitmap(picture);
c.drawBitmap(data.image, matrix, paint);
@@ -171,7 +171,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Note, we can't use the preview for the small icon, since it is non-square
float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
- Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, data.image.getConfig());
+ Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
matrix.setScale(scale, scale);
matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
(iconSize - (scale * mImageHeight)) / 2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index a53e348fd16c..fb9c037c236c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -252,6 +252,13 @@ public class ScrimView extends View implements ConfigurationController.Configura
return false;
}
+ /**
+ * It might look counterintuitive to have another method to set the alpha instead of
+ * only using {@link #setAlpha(float)}. In this case we're in a hardware layer
+ * optimizing blend modes, so it makes sense.
+ *
+ * @param alpha Gradient alpha from 0 to 1.
+ */
public void setViewAlpha(float alpha) {
if (alpha != mViewAlpha) {
mViewAlpha = alpha;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
index eb211a10737b..4d58cb88dd13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
@@ -58,6 +58,6 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
@Override
public boolean isDimmable() {
- return false;
+ return getCustomBackgroundColor() == 0;
}
}
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 6b7397b3f8ca..3f57c2f9384f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -46,10 +46,8 @@ public class DozeParameters {
public void dump(PrintWriter pw) {
pw.println(" DozeParameters:");
pw.print(" getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
- pw.print(" getPulseDuration(pickup=false): "); pw.println(getPulseDuration(false));
- pw.print(" getPulseDuration(pickup=true): "); pw.println(getPulseDuration(true));
- pw.print(" getPulseInDuration(pickup=false): "); pw.println(getPulseInDuration(false));
- pw.print(" getPulseInDuration(pickup=true): "); pw.println(getPulseInDuration(true));
+ pw.print(" getPulseDuration(): "); pw.println(getPulseDuration());
+ pw.print(" getPulseInDuration(): "); pw.println(getPulseInDuration());
pw.print(" getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration());
pw.print(" getPulseOutDuration(): "); pw.println(getPulseOutDuration());
pw.print(" getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
@@ -81,14 +79,12 @@ public class DozeParameters {
return mContext.getResources().getBoolean(R.bool.doze_suspend_display_state_supported);
}
- public int getPulseDuration(boolean pickup) {
- return getPulseInDuration(pickup) + getPulseVisibleDuration() + getPulseOutDuration();
+ public int getPulseDuration() {
+ return getPulseInDuration() + getPulseVisibleDuration() + getPulseOutDuration();
}
- public int getPulseInDuration(boolean pickupOrDoubleTap) {
- return pickupOrDoubleTap
- ? getInt("doze.pulse.duration.in.pickup", R.integer.doze_pulse_duration_in_pickup)
- : getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
+ public int getPulseInDuration() {
+ return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
}
public int getPulseVisibleDuration() {
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 8afb8490808e..1011383b72e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -16,16 +16,11 @@
package com.android.systemui.statusbar.phone;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
-import android.view.animation.Interpolator;
-import com.android.systemui.Interpolators;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
@@ -40,74 +35,59 @@ public class DozeScrimController {
private final Handler mHandler = new Handler();
private final ScrimController mScrimController;
- private final Context mContext;
-
private boolean mDozing;
private DozeHost.PulseCallback mPulseCallback;
private int mPulseReason;
- private Animator mInFrontAnimator;
- private Animator mBehindAnimator;
- private float mInFrontTarget;
- private float mBehindTarget;
- private boolean mDozingAborted;
- private boolean mWakeAndUnlocking;
private boolean mFullyPulsing;
- private float mAodFrontScrimOpacity = 0;
- private Runnable mSetDozeInFrontAlphaDelayed;
+ private final ScrimController.Callback mScrimCallback = new ScrimController.Callback() {
+ @Override
+ public void onDisplayBlanked() {
+ if (DEBUG) {
+ Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
+ + DozeLog.pulseReasonToString(mPulseReason));
+ }
+ if (!mDozing) {
+ return;
+ }
+
+ // Signal that the pulse is ready to turn the screen on and draw.
+ pulseStarted();
+ }
+
+ @Override
+ public void onFinished() {
+ if (DEBUG) {
+ Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
+ }
+ if (!mDozing) {
+ return;
+ }
+ mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+ mHandler.postDelayed(mPulseOutExtended,
+ mDozeParameters.getPulseVisibleDurationExtended());
+ mFullyPulsing = true;
+ }
+
+ /**
+ * Transition was aborted before it was over.
+ */
+ @Override
+ public void onCancelled() {
+ pulseFinished();
+ }
+ };
public DozeScrimController(ScrimController scrimController, Context context) {
- mContext = context;
mScrimController = scrimController;
mDozeParameters = new DozeParameters(context);
}
- public void setDozing(boolean dozing, boolean animate) {
+ public void setDozing(boolean dozing) {
if (mDozing == dozing) return;
mDozing = dozing;
- mWakeAndUnlocking = false;
- if (mDozing) {
- mDozingAborted = false;
- abortAnimations();
- mScrimController.setDozeBehindAlpha(1f);
- setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? mAodFrontScrimOpacity : 1f);
- } else {
+ if (!mDozing) {
cancelPulsing();
- if (animate) {
- startScrimAnimation(false /* inFront */, 0f /* target */,
- NotificationPanelView.DOZE_ANIMATION_DURATION,
- Interpolators.LINEAR_OUT_SLOW_IN);
- startScrimAnimation(true /* inFront */, 0f /* target */,
- NotificationPanelView.DOZE_ANIMATION_DURATION,
- Interpolators.LINEAR_OUT_SLOW_IN);
- } else {
- abortAnimations();
- mScrimController.setDozeBehindAlpha(0f);
- setDozeInFrontAlpha(0f);
- }
- }
- }
-
- /**
- * Set the opacity of the front scrim when showing AOD1
- *
- * Used to emulate lower brightness values than the hardware supports natively.
- */
- public void setAodDimmingScrim(float scrimOpacity) {
- mAodFrontScrimOpacity = scrimOpacity;
- if (mDozing && !isPulsing() && !mDozingAborted && !mWakeAndUnlocking
- && mDozeParameters.getAlwaysOn()) {
- setDozeInFrontAlpha(mAodFrontScrimOpacity);
- }
- }
-
- public void setWakeAndUnlocking() {
- // Immediately abort the doze scrims in case of wake-and-unlock
- // for pulsing so the Keyguard fade-out animation scrim can take over.
- if (!mWakeAndUnlocking) {
- mWakeAndUnlocking = true;
- mScrimController.setDozeBehindAlpha(0f);
- setDozeInFrontAlpha(0f);
}
}
@@ -118,37 +98,21 @@ public class DozeScrimController {
}
if (!mDozing || mPulseCallback != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Pulse supressed. Dozing: " + mDozeParameters + " had callback? "
+ + (mPulseCallback != null));
+ }
// Pulse suppressed.
callback.onPulseFinished();
return;
}
- // Begin pulse. Note that it's very important that the pulse finished callback
+ // Begin pulse. Note that it's very important that the pulse finished callback
// be invoked when we're done so that the caller can drop the pulse wakelock.
mPulseCallback = callback;
mPulseReason = reason;
- setDozeInFrontAlpha(1f);
- mHandler.post(mPulseIn);
- }
-
- /**
- * Aborts pulsing immediately.
- */
- public void abortPulsing() {
- cancelPulsing();
- if (mDozing && !mWakeAndUnlocking) {
- mScrimController.setDozeBehindAlpha(1f);
- setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() && !mDozingAborted
- ? mAodFrontScrimOpacity : 1f);
- }
- }
- /**
- * Aborts dozing immediately.
- */
- public void abortDoze() {
- mDozingAborted = true;
- abortPulsing();
+ mScrimController.transitionTo(ScrimState.PULSING, mScrimCallback);
}
public void pulseOutNow() {
@@ -157,17 +121,6 @@ public class DozeScrimController {
}
}
- public void onScreenTurnedOn() {
- if (isPulsing()) {
- final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
- || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
- startScrimAnimation(true /* inFront */, 0f,
- mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
- pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
- mPulseInFinished);
- }
- }
-
public boolean isPulsing() {
return mPulseCallback != null;
}
@@ -181,11 +134,9 @@ public class DozeScrimController {
}
private void cancelPulsing() {
- if (DEBUG) Log.d(TAG, "Cancel pulsing");
-
if (mPulseCallback != null) {
+ if (DEBUG) Log.d(TAG, "Cancel pulsing");
mFullyPulsing = false;
- mHandler.removeCallbacks(mPulseIn);
mHandler.removeCallbacks(mPulseOut);
mHandler.removeCallbacks(mPulseOutExtended);
pulseFinished();
@@ -193,151 +144,20 @@ public class DozeScrimController {
}
private void pulseStarted() {
+ DozeLog.tracePulseStart(mPulseReason);
if (mPulseCallback != null) {
mPulseCallback.onPulseStarted();
}
}
private void pulseFinished() {
+ DozeLog.tracePulseFinish();
if (mPulseCallback != null) {
mPulseCallback.onPulseFinished();
mPulseCallback = null;
}
}
- private void abortAnimations() {
- if (mInFrontAnimator != null) {
- mInFrontAnimator.cancel();
- }
- if (mBehindAnimator != null) {
- mBehindAnimator.cancel();
- }
- }
-
- private void startScrimAnimation(final boolean inFront, float target, long duration,
- Interpolator interpolator) {
- startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
- }
-
- private void startScrimAnimation(final boolean inFront, float target, long duration,
- Interpolator interpolator, final Runnable endRunnable) {
- Animator current = getCurrentAnimator(inFront);
- if (current != null) {
- float currentTarget = getCurrentTarget(inFront);
- if (currentTarget == target) {
- return;
- }
- current.cancel();
- }
- ValueAnimator anim = ValueAnimator.ofFloat(getDozeAlpha(inFront), target);
- anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float value = (float) animation.getAnimatedValue();
- setDozeAlpha(inFront, value);
- }
- });
- anim.setInterpolator(interpolator);
- anim.setDuration(duration);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- setCurrentAnimator(inFront, null);
- if (endRunnable != null) {
- endRunnable.run();
- }
- }
- });
- anim.start();
- setCurrentAnimator(inFront, anim);
- setCurrentTarget(inFront, target);
- }
-
- private float getCurrentTarget(boolean inFront) {
- return inFront ? mInFrontTarget : mBehindTarget;
- }
-
- private void setCurrentTarget(boolean inFront, float target) {
- if (inFront) {
- mInFrontTarget = target;
- } else {
- mBehindTarget = target;
- }
- }
-
- private Animator getCurrentAnimator(boolean inFront) {
- return inFront ? mInFrontAnimator : mBehindAnimator;
- }
-
- private void setCurrentAnimator(boolean inFront, Animator animator) {
- if (inFront) {
- mInFrontAnimator = animator;
- } else {
- mBehindAnimator = animator;
- }
- }
-
- private void setDozeAlpha(boolean inFront, float alpha) {
- if (mWakeAndUnlocking) {
- return;
- }
- if (inFront) {
- mScrimController.setDozeInFrontAlpha(alpha);
- } else {
- mScrimController.setDozeBehindAlpha(alpha);
- }
- }
-
- private float getDozeAlpha(boolean inFront) {
- return inFront
- ? mScrimController.getDozeInFrontAlpha()
- : mScrimController.getDozeBehindAlpha();
- }
-
- private void setDozeInFrontAlpha(float opacity) {
- setDozeInFrontAlphaDelayed(opacity, 0 /* delay */);
-
- }
-
- private void setDozeInFrontAlphaDelayed(float opacity, long delayMs) {
- if (mSetDozeInFrontAlphaDelayed != null) {
- mHandler.removeCallbacks(mSetDozeInFrontAlphaDelayed);
- mSetDozeInFrontAlphaDelayed = null;
- }
- if (delayMs <= 0) {
- mScrimController.setDozeInFrontAlpha(opacity);
- } else {
- mHandler.postDelayed(mSetDozeInFrontAlphaDelayed = () -> {
- setDozeInFrontAlpha(opacity);
- }, delayMs);
- }
- }
-
- private final Runnable mPulseIn = new Runnable() {
- @Override
- public void run() {
- if (DEBUG) Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
- + DozeLog.pulseReasonToString(mPulseReason));
- if (!mDozing) return;
- DozeLog.tracePulseStart(mPulseReason);
-
- // Signal that the pulse is ready to turn the screen on and draw.
- pulseStarted();
- }
- };
-
- private final Runnable mPulseInFinished = new Runnable() {
- @Override
- public void run() {
- if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
- if (!mDozing) return;
- mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
- mHandler.postDelayed(mPulseOutExtended,
- mDozeParameters.getPulseVisibleDurationExtended());
- mFullyPulsing = true;
- }
- };
-
private final Runnable mPulseOutExtended = new Runnable() {
@Override
public void run() {
@@ -354,38 +174,13 @@ public class DozeScrimController {
mHandler.removeCallbacks(mPulseOutExtended);
if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
if (!mDozing) return;
- startScrimAnimation(true /* inFront */, 1,
- mDozeParameters.getPulseOutDuration(),
- Interpolators.ALPHA_IN, mPulseOutFinishing);
- }
- };
-
- private final Runnable mPulseOutFinishing = new Runnable() {
- @Override
- public void run() {
- if (DEBUG) Log.d(TAG, "Pulse out finished");
- DozeLog.tracePulseFinish();
- if (mDozeParameters.getAlwaysOn() && mDozing) {
- // Setting power states can block rendering. For AOD, delay finishing the pulse and
- // setting the power state until the fully black scrim had time to hit the
- // framebuffer.
- mHandler.postDelayed(mPulseOutFinished, 30);
- } else {
- mPulseOutFinished.run();
- }
- }
- };
-
- private final Runnable mPulseOutFinished = new Runnable() {
- @Override
- public void run() {
- // Signal that the pulse is all finished so we can turn the screen off now.
- DozeScrimController.this.pulseFinished();
- if (mDozeParameters.getAlwaysOn()) {
- // Setting power states can happen after we push out the frame. Make sure we
- // stay fully opaque until the power state request reaches the lower levels.
- setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 100);
- }
+ mScrimController.transitionTo(ScrimState.AOD,
+ new ScrimController.Callback() {
+ @Override
+ public void onDisplayBlanked() {
+ pulseFinished();
+ }
+ });
}
};
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 91369dbd5f89..80d4061b3864 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -181,9 +181,9 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
}
private boolean pulsingOrAod() {
- boolean pulsing = mDozeScrimController.isPulsing();
- boolean dozingWithScreenOn = mStatusBar.isDozing() && !mStatusBar.isScreenFullyOff();
- return pulsing || dozingWithScreenOn;
+ final ScrimState scrimState = mScrimController.getState();
+ return scrimState == ScrimState.AOD
+ || scrimState == ScrimState.PULSING;
}
@Override
@@ -246,15 +246,12 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
true /* allowEnterAnimation */);
} else if (mMode == MODE_WAKE_AND_UNLOCK){
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
- mDozeScrimController.abortDoze();
} else {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
mUpdateMonitor.awakenFromDream();
}
mStatusBarWindowManager.setStatusBarFocusable(false);
mKeyguardViewMediator.onWakeAndUnlocking();
- mScrimController.setWakeAndUnlocking();
- mDozeScrimController.setWakeAndUnlocking();
if (mStatusBar.getNavigationBarView() != null) {
mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
}
@@ -269,6 +266,7 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
}
private void showBouncer() {
+ mScrimController.transitionTo(ScrimState.BOUNCER);
mStatusBarKeyguardViewManager.animateCollapsePanels(
FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
mPendingShowBouncer = false;
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 702afa3a38b1..dfd4c1768268 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -25,7 +25,9 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.os.Trace;
+import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -34,12 +36,14 @@ import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener;
import com.android.internal.graphics.ColorUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -47,7 +51,10 @@ import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.util.wakelock.DelayedWakeLock;
+import com.android.systemui.util.wakelock.WakeLock;
+import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -56,33 +63,54 @@ import java.util.function.Consumer;
* security method gets shown).
*/
public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
- OnHeadsUpChangedListener, OnColorsChangedListener {
+ OnHeadsUpChangedListener, OnColorsChangedListener, Dumpable {
+
+ private static final String TAG = "ScrimController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
public static final long ANIMATION_DURATION = 220;
public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
= new PathInterpolator(0f, 0, 0.7f, 1f);
public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
= new PathInterpolator(0.3f, 0f, 0.8f, 1f);
- // Default alpha value for most scrims, if unsure use this constant
+ /**
+ * Default alpha value for most scrims.
+ */
public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
- // A scrim varies its opacity based on a busyness factor, for example
- // how many notifications are currently visible.
+ /**
+ * A scrim varies its opacity based on a busyness factor, for example
+ * how many notifications are currently visible.
+ */
public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f;
+ /**
+ * The most common scrim, the one under the keyguard.
+ */
protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
+ /**
+ * We fade out the bottom scrim when the bouncer is visible.
+ */
protected static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
- private static final float SCRIM_IN_FRONT_ALPHA = GRADIENT_SCRIM_ALPHA_BUSY;
- private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY;
- private static final int TAG_KEY_ANIM = R.id.scrim;
+ /**
+ * Opacity of the scrim behind the bouncer (the one doing actual background protection.)
+ */
+ protected static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY;
+
+ static final int TAG_KEY_ANIM = R.id.scrim;
+ static final int TAG_KEY_ANIM_BLANK = R.id.scrim_blanking;
private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
private static final float NOT_INITIALIZED = -1;
- private final LightBarController mLightBarController;
+ private ScrimState mState = ScrimState.UNINITIALIZED;
+ private final Context mContext;
protected final ScrimView mScrimBehind;
protected final ScrimView mScrimInFront;
- private final UnlockMethodCache mUnlockMethodCache;
private final View mHeadsUpScrim;
+ private final LightBarController mLightBarController;
+ private final UnlockMethodCache mUnlockMethodCache;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final DozeParameters mDozeParameters;
private final SysuiColorExtractor mColorExtractor;
private GradientColors mLockColors;
@@ -94,61 +122,53 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
- protected boolean mKeyguardShowing;
private float mFraction;
private boolean mDarkenWhileDragging;
- protected boolean mBouncerShowing;
- protected boolean mBouncerIsKeyguard = false;
- private boolean mWakeAndUnlocking;
protected boolean mAnimateChange;
private boolean mUpdatePending;
private boolean mTracking;
private boolean mAnimateKeyguardFadingOut;
- protected long mDurationOverride = -1;
+ protected long mAnimationDuration = -1;
private long mAnimationDelay;
private Runnable mOnAnimationFinished;
private boolean mDeferFinishedListener;
private final Interpolator mInterpolator = new DecelerateInterpolator();
- private boolean mDozing;
- private float mDozeInFrontAlpha;
- private float mDozeBehindAlpha;
private float mCurrentInFrontAlpha = NOT_INITIALIZED;
private float mCurrentBehindAlpha = NOT_INITIALIZED;
- private float mCurrentHeadsUpAlpha = NOT_INITIALIZED;
+ private int mCurrentInFrontTint;
+ private int mCurrentBehindTint;
private int mPinnedHeadsUpCount;
private float mTopHeadsUpDragAmount;
private View mDraggedHeadsUpView;
- private boolean mForceHideScrims;
- private boolean mSkipFirstFrame;
- private boolean mDontAnimateBouncerChanges;
private boolean mKeyguardFadingOutInProgress;
- private boolean mAnimatingDozeUnlock;
private ValueAnimator mKeyguardFadeoutAnimation;
- /** Wake up from AOD transition is starting; need fully opaque front scrim */
- private boolean mWakingUpFromAodStarting;
- /** Wake up from AOD transition is in progress; need black tint */
- private boolean mWakingUpFromAodInProgress;
- /** Wake up from AOD transition is animating; need to reset when animation finishes */
- private boolean mWakingUpFromAodAnimationRunning;
- private boolean mScrimsVisble;
+ private boolean mScrimsVisible;
private final Consumer<Boolean> mScrimVisibleListener;
+ private boolean mBlankScreen;
+ private boolean mScreenBlankingCallbackCalled;
+ private Callback mCallback;
+
+ private final WakeLock mWakeLock;
+ private boolean mWakeLockHeld;
public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
- ScrimView scrimInFront, View headsUpScrim,
- Consumer<Boolean> scrimVisibleListener) {
+ ScrimView scrimInFront, View headsUpScrim, Consumer<Boolean> scrimVisibleListener,
+ DozeParameters dozeParameters) {
mScrimBehind = scrimBehind;
mScrimInFront = scrimInFront;
mHeadsUpScrim = headsUpScrim;
mScrimVisibleListener = scrimVisibleListener;
- final Context context = scrimBehind.getContext();
- mUnlockMethodCache = UnlockMethodCache.getInstance(context);
- mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+ mContext = scrimBehind.getContext();
+ mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
mLightBarController = lightBarController;
- mScrimBehindAlphaResValue = context.getResources().getFloat(R.dimen.scrim_behind_alpha);
+ mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha);
+ mWakeLock = createWakeLock();
// 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;
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mColorExtractor.addOnColorsChangedListener(this);
@@ -158,159 +178,155 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
ColorExtractor.TYPE_DARK, true /* ignoreVisibility */);
mNeedsDrawableColorUpdate = true;
+ final ScrimState[] states = ScrimState.values();
+ for (int i = 0; i < states.length; i++) {
+ states[i].init(mScrimInFront, mScrimBehind, mDozeParameters);
+ states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
+ }
+ mState = ScrimState.UNINITIALIZED;
+
updateHeadsUpScrim(false);
updateScrims();
}
- public void setKeyguardShowing(boolean showing) {
- mKeyguardShowing = showing;
-
- // Showing/hiding the keyguard means that scrim colors have to be switched
- mNeedsDrawableColorUpdate = true;
- scheduleUpdate();
- }
-
- protected void setScrimBehindValues(float scrimBehindAlphaKeyguard,
- float scrimBehindAlphaUnlocking) {
- mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
- mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking;
- scheduleUpdate();
- }
-
- public void onTrackingStarted() {
- mTracking = true;
- mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
- }
-
- public void onExpandingFinished() {
- mTracking = false;
+ public void transitionTo(ScrimState state) {
+ transitionTo(state, null);
}
- public void setPanelExpansion(float fraction) {
- if (mFraction != fraction) {
- mFraction = fraction;
- scheduleUpdate();
- if (mPinnedHeadsUpCount != 0) {
- updateHeadsUpScrim(false);
- }
- if (mKeyguardFadeoutAnimation != null && mTracking) {
- mKeyguardFadeoutAnimation.cancel();
- }
+ public void transitionTo(ScrimState state, Callback callback) {
+ if (state == mState) {
+ return;
+ } else if (DEBUG) {
+ Log.d(TAG, "State changed to: " + state);
}
- }
-
- public void setBouncerShowing(boolean showing) {
- mBouncerShowing = showing;
- mAnimateChange = !mTracking && !mDontAnimateBouncerChanges && !mKeyguardFadingOutInProgress;
- scheduleUpdate();
- }
- /** Prepares the wakeUpFromAod animation (while turning on screen); Forces black scrims. */
- public void prepareWakeUpFromAod() {
- if (mWakingUpFromAodInProgress) {
- return;
+ if (state == ScrimState.UNINITIALIZED) {
+ throw new IllegalArgumentException("Cannot change to UNINITIALIZED.");
}
- mWakingUpFromAodInProgress = true;
- mWakingUpFromAodStarting = true;
- mAnimateChange = false;
- scheduleUpdate();
- onPreDraw();
- }
- /** Starts the wakeUpFromAod animation (once screen is on); animate to transparent scrims. */
- public void wakeUpFromAod() {
- if (mWakeAndUnlocking || mAnimateKeyguardFadingOut) {
- // Wake and unlocking has a separate transition that must not be interfered with.
- mWakingUpFromAodStarting = false;
- mWakingUpFromAodInProgress = false;
- return;
+ if (mCallback != null) {
+ mCallback.onCancelled();
}
- if (mWakingUpFromAodStarting) {
- mWakingUpFromAodInProgress = true;
- mWakingUpFromAodStarting = false;
- mAnimateChange = true;
- scheduleUpdate();
+ mCallback = callback;
+
+ state.prepare(mState);
+ mScreenBlankingCallbackCalled = false;
+ mAnimationDelay = 0;
+ mBlankScreen = state.getBlanksScreen();
+ mAnimateChange = state.getAnimateChange();
+ mAnimationDuration = state.getAnimationDuration();
+ mCurrentInFrontTint = state.getFrontTint();
+ mCurrentBehindTint = state.getBehindTint();
+ mCurrentInFrontAlpha = state.getFrontAlpha();
+ mCurrentBehindAlpha = state.getBehindAlpha();
+
+ // Showing/hiding the keyguard means that scrim colors have to be switched, not necessary
+ // to do the same when you're just showing the brightness mirror.
+ mNeedsDrawableColorUpdate = state != ScrimState.BRIGHTNESS_MIRROR;
+
+ if (mKeyguardFadeoutAnimation != null) {
+ mKeyguardFadeoutAnimation.cancel();
}
- }
- public void setWakeAndUnlocking() {
- mWakeAndUnlocking = true;
- mAnimatingDozeUnlock = true;
- mWakingUpFromAodStarting = false;
- mWakingUpFromAodInProgress = false;
- scheduleUpdate();
- }
+ mState = state;
- public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished,
- boolean skipFirstFrame) {
- mWakeAndUnlocking = false;
- mAnimateKeyguardFadingOut = true;
- mDurationOverride = duration;
- mAnimationDelay = delay;
- mAnimateChange = true;
- mSkipFirstFrame = skipFirstFrame;
- mOnAnimationFinished = onAnimationFinished;
+ // Do not let the device sleep until we're done with all animations
+ if (!mWakeLockHeld) {
+ if (mWakeLock != null) {
+ mWakeLockHeld = true;
+ mWakeLock.acquire();
+ } else {
+ Log.w(TAG, "Cannot hold wake lock, it has not been set yet");
+ }
+ }
if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
scheduleUpdate();
-
- // No need to wait for the next frame to be drawn for this case - onPreDraw will execute
- // the changes we just scheduled.
- onPreDraw();
} else {
-
// In case the user isn't unlocked, make sure to delay a bit because the system is hosed
- // with too many things in this case, in order to not skip the initial frames.
+ // with too many things at this case, in order to not skip the initial frames.
mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
+ mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
}
}
- public void abortKeyguardFadingOut() {
- if (mAnimateKeyguardFadingOut) {
- endAnimateKeyguardFadingOut(true /* force */);
- }
+ public ScrimState getState() {
+ return mState;
}
- public void animateKeyguardUnoccluding(long duration) {
- mAnimateChange = false;
- setScrimBehindAlpha(0f);
- mAnimateChange = true;
+ protected void setScrimBehindValues(float scrimBehindAlphaKeyguard,
+ float scrimBehindAlphaUnlocking) {
+ mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
+ mScrimBehindAlphaUnlocking = scrimBehindAlphaUnlocking;
+ ScrimState[] states = ScrimState.values();
+ for (int i = 0; i < states.length; i++) {
+ states[i].setScrimBehindAlphaKeyguard(scrimBehindAlphaKeyguard);
+ }
scheduleUpdate();
- mDurationOverride = duration;
}
- public void animateGoingToFullShade(long delay, long duration) {
- mDurationOverride = duration;
- mAnimationDelay = delay;
- mAnimateChange = true;
- scheduleUpdate();
+ public void onTrackingStarted() {
+ mTracking = true;
+ mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
}
- public void setDozing(boolean dozing) {
- if (mDozing != dozing) {
- mDozing = dozing;
- scheduleUpdate();
- }
+ public void onExpandingFinished() {
+ mTracking = false;
}
- public void setDozeInFrontAlpha(float alpha) {
- mDozeInFrontAlpha = alpha;
- updateScrimColor(mScrimInFront);
- }
+ /**
+ * Current state of the shade expansion when pulling it from the top.
+ * This value is 1 when on top of the keyguard and goes to 0 as the user drags up.
+ *
+ * The expansion fraction is tied to the scrim opacity.
+ *
+ * @param fraction From 0 to 1 where 0 means collapse and 1 expanded.
+ */
+ public void setPanelExpansion(float fraction) {
+ if (mFraction != fraction) {
+ mFraction = fraction;
- public void setDozeBehindAlpha(float alpha) {
- mDozeBehindAlpha = alpha;
- updateScrimColor(mScrimBehind);
- }
+ if (mState == ScrimState.UNLOCKED) {
+ // Darken scrim as you pull down the shade when unlocked
+ float behindFraction = getInterpolatedFraction();
+ behindFraction = (float) Math.pow(behindFraction, 0.8f);
+ mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
+ mCurrentInFrontAlpha = 0;
+ } else if (mState == ScrimState.KEYGUARD) {
+ if (mUpdatePending) {
+ return;
+ }
- public float getDozeBehindAlpha() {
- return mDozeBehindAlpha;
- }
+ // Either darken of make the scrim transparent when you
+ // pull down the shade
+ float interpolatedFract = getInterpolatedFraction();
+ if (mDarkenWhileDragging) {
+ mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
+ mScrimBehindAlphaKeyguard, interpolatedFract);
+ mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
+ } else {
+ mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
+ interpolatedFract);
+ mCurrentInFrontAlpha = 0;
+ }
+ } else {
+ Log.w(TAG, "Invalid state, cannot set panel expansion when: " + mState);
+ return;
+ }
+
+ if (mPinnedHeadsUpCount != 0) {
+ updateHeadsUpScrim(false);
+ }
- public float getDozeInFrontAlpha() {
- return mDozeInFrontAlpha;
+ updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha);
+ updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha);
+ }
}
+ /**
+ * Keyguard and shade scrim opacity varies according to how many notifications are visible.
+ * @param notificationCount Number of visible notifications.
+ */
public void setNotificationCount(int notificationCount) {
final float maxNotificationDensity = 3;
float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f);
@@ -319,15 +335,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
notificationDensity);
if (mScrimBehindAlphaKeyguard != newAlpha) {
mScrimBehindAlphaKeyguard = newAlpha;
- mAnimateChange = true;
- scheduleUpdate();
- }
- }
- private float getScrimInFrontAlpha() {
- return mKeyguardUpdateMonitor.needsSlowUnlockTransition()
- ? SCRIM_IN_FRONT_ALPHA_LOCKED
- : SCRIM_IN_FRONT_ALPHA;
+ if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER) {
+ scheduleUpdate();
+ }
+ }
}
/**
@@ -352,7 +364,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
if (mNeedsDrawableColorUpdate) {
mNeedsDrawableColorUpdate = false;
final GradientColors currentScrimColors;
- if (mKeyguardShowing) {
+ if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER) {
// Always animate color changes if we're seeing the keyguard
mScrimInFront.setColors(mLockColors, true /* animated */);
mScrimBehind.setColors(mLockColors, true /* animated */);
@@ -375,77 +387,31 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
mLightBarController.setScrimColor(mScrimInFront.getColors());
}
- if (mAnimateKeyguardFadingOut || mForceHideScrims) {
- setScrimInFrontAlpha(0f);
- setScrimBehindAlpha(0f);
- } else if (mWakeAndUnlocking) {
- // During wake and unlock, we first hide everything behind a black scrim, which then
- // gets faded out from animateKeyguardFadingOut. This must never be animated.
- mAnimateChange = false;
- if (mDozing) {
- setScrimInFrontAlpha(0f);
- setScrimBehindAlpha(1f);
- } else {
- setScrimInFrontAlpha(1f);
- setScrimBehindAlpha(0f);
- }
- } else if (!mKeyguardShowing && !mBouncerShowing && !mWakingUpFromAodStarting) {
- updateScrimNormal();
- setScrimInFrontAlpha(0);
- } else {
- updateScrimKeyguard();
- }
- mAnimateChange = false;
+ setScrimInFrontAlpha(mCurrentInFrontAlpha);
+ setScrimBehindAlpha(mCurrentBehindAlpha);
+
dispatchScrimsVisible();
}
private void dispatchScrimsVisible() {
boolean scrimsVisible = mScrimBehind.getViewAlpha() > 0 || mScrimInFront.getViewAlpha() > 0;
- if (mScrimsVisble != scrimsVisible) {
- mScrimsVisble = scrimsVisible;
+ if (mScrimsVisible != scrimsVisible) {
+ mScrimsVisible = scrimsVisible;
mScrimVisibleListener.accept(scrimsVisible);
}
}
- private void updateScrimKeyguard() {
- if (mTracking && mDarkenWhileDragging) {
- float behindFraction = Math.max(0, Math.min(mFraction, 1));
- float fraction = 1 - behindFraction;
- fraction = (float) Math.pow(fraction, 0.8f);
- behindFraction = (float) Math.pow(behindFraction, 0.8f);
- setScrimInFrontAlpha(fraction * getScrimInFrontAlpha());
- setScrimBehindAlpha(behindFraction * mScrimBehindAlphaKeyguard);
- } else if (mBouncerShowing && !mBouncerIsKeyguard) {
- setScrimInFrontAlpha(getScrimInFrontAlpha());
- updateScrimNormal();
- } else if (mBouncerShowing) {
- setScrimInFrontAlpha(0f);
- setScrimBehindAlpha(mScrimBehindAlpha);
- } else {
- float fraction = Math.max(0, Math.min(mFraction, 1));
- if (mWakingUpFromAodStarting) {
- setScrimInFrontAlpha(1f);
- } else {
- setScrimInFrontAlpha(0f);
- }
- setScrimBehindAlpha(fraction
- * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking)
- + mScrimBehindAlphaUnlocking);
- }
- }
-
- private void updateScrimNormal() {
+ private float getInterpolatedFraction() {
float frac = mFraction;
// let's start this 20% of the way down the screen
frac = frac * 1.2f - 0.2f;
if (frac <= 0) {
- setScrimBehindAlpha(0);
+ return 0;
} else {
// woo, special effects
- final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
- setScrimBehindAlpha(k * mScrimBehindAlpha);
+ return (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
}
}
@@ -455,102 +421,76 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
private void setScrimInFrontAlpha(float alpha) {
setScrimAlpha(mScrimInFront, alpha);
- if (alpha == 0f) {
- mScrimInFront.setClickable(false);
- } else {
- // Eat touch events (unless dozing).
- mScrimInFront.setClickable(!mDozing);
- }
}
private void setScrimAlpha(View scrim, float alpha) {
- updateScrim(mAnimateChange, scrim, alpha, getCurrentScrimAlpha(scrim));
- }
-
- protected float getDozeAlpha(View scrim) {
- return scrim == mScrimBehind ? mDozeBehindAlpha : mDozeInFrontAlpha;
- }
-
- protected float getCurrentScrimAlpha(View scrim) {
- return scrim == mScrimBehind ? mCurrentBehindAlpha
- : scrim == mScrimInFront ? mCurrentInFrontAlpha
- : mCurrentHeadsUpAlpha;
- }
-
- private void setCurrentScrimAlpha(View scrim, float alpha) {
- if (scrim == mScrimBehind) {
- mCurrentBehindAlpha = alpha;
- mLightBarController.setScrimAlpha(mCurrentBehindAlpha);
- } else if (scrim == mScrimInFront) {
- mCurrentInFrontAlpha = alpha;
+ if (alpha == 0f) {
+ scrim.setClickable(false);
} else {
- alpha = Math.max(0.0f, Math.min(1.0f, alpha));
- mCurrentHeadsUpAlpha = alpha;
+ // Eat touch events (unless dozing).
+ scrim.setClickable(!(mState == ScrimState.AOD));
}
+ updateScrim(mAnimateChange, scrim, alpha);
}
- private void updateScrimColor(View scrim) {
- float alpha1 = getCurrentScrimAlpha(scrim);
+ private void updateScrimColor(View scrim, float alpha, int tint) {
+ alpha = Math.max(0, Math.min(1.0f, alpha));
if (scrim instanceof ScrimView) {
ScrimView scrimView = (ScrimView) scrim;
- float dozeAlpha = getDozeAlpha(scrim);
- float alpha = 1 - (1 - alpha1) * (1 - dozeAlpha);
- alpha = Math.max(0, Math.min(1.0f, alpha));
- scrimView.setViewAlpha(alpha);
Trace.traceCounter(Trace.TRACE_TAG_APP,
scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha",
(int) (alpha * 255));
- int dozeTint = Color.TRANSPARENT;
-
- boolean dozing = mAnimatingDozeUnlock || mDozing;
- boolean frontScrimDozing = mWakingUpFromAodInProgress;
- if (dozing || frontScrimDozing && scrim == mScrimInFront) {
- dozeTint = Color.BLACK;
- }
Trace.traceCounter(Trace.TRACE_TAG_APP,
scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint",
- dozeTint == Color.BLACK ? 1 : 0);
+ Color.alpha(tint));
- scrimView.setTint(dozeTint);
+ scrimView.setTint(tint);
+ scrimView.setViewAlpha(alpha);
} else {
- scrim.setAlpha(alpha1);
+ scrim.setAlpha(alpha);
}
dispatchScrimsVisible();
}
- private void startScrimAnimation(final View scrim, float target) {
- float current = getCurrentScrimAlpha(scrim);
- ValueAnimator anim = ValueAnimator.ofFloat(current, target);
+ private int getCurrentScrimTint(View scrim) {
+ return scrim == mScrimInFront ? mCurrentInFrontTint : mCurrentBehindTint;
+ }
+
+ private void startScrimAnimation(final View scrim, float current, float target) {
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() :
+ Color.TRANSPARENT;
anim.addUpdateListener(animation -> {
- float alpha = (float) animation.getAnimatedValue();
- setCurrentScrimAlpha(scrim, alpha);
- updateScrimColor(scrim);
+ final float animAmount = (float) animation.getAnimatedValue();
+ final int finalScrimTint = scrim == mScrimInFront ?
+ mCurrentInFrontTint : mCurrentBehindTint;
+ float alpha = MathUtils.lerp(current, target, animAmount);
+ int tint = ColorUtils.blendARGB(initialScrimTint, finalScrimTint, animAmount);
+ updateScrimColor(scrim, alpha, tint);
dispatchScrimsVisible();
});
anim.setInterpolator(getInterpolator());
anim.setStartDelay(mAnimationDelay);
- anim.setDuration(mDurationOverride != -1 ? mDurationOverride : ANIMATION_DURATION);
+ anim.setDuration(mAnimationDuration);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- if (!mDeferFinishedListener && mOnAnimationFinished != null) {
- mOnAnimationFinished.run();
- mOnAnimationFinished = null;
- }
if (mKeyguardFadingOutInProgress) {
mKeyguardFadeoutAnimation = null;
mKeyguardFadingOutInProgress = false;
- mAnimatingDozeUnlock = false;
- }
- if (mWakingUpFromAodAnimationRunning && !mDeferFinishedListener) {
- mWakingUpFromAodAnimationRunning = false;
- mWakingUpFromAodInProgress = false;
}
+ onFinished();
+
scrim.setTag(TAG_KEY_ANIM, null);
scrim.setTag(TAG_KEY_ANIM_TARGET, null);
dispatchScrimsVisible();
+
+ if (!mDeferFinishedListener && mOnAnimationFinished != null) {
+ mOnAnimationFinished.run();
+ mOnAnimationFinished = null;
+ }
}
});
anim.start();
@@ -558,12 +498,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
mKeyguardFadingOutInProgress = true;
mKeyguardFadeoutAnimation = anim;
}
- if (mWakingUpFromAodInProgress) {
- mWakingUpFromAodAnimationRunning = true;
- }
- if (mSkipFirstFrame) {
- anim.setCurrentPlayTime(16);
- }
scrim.setTag(TAG_KEY_ANIM, anim);
scrim.setTag(TAG_KEY_ANIM_TARGET, target);
}
@@ -582,19 +516,33 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
public boolean onPreDraw() {
mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
mUpdatePending = false;
- if (mDontAnimateBouncerChanges) {
- mDontAnimateBouncerChanges = false;
+ if (mCallback != null) {
+ mCallback.onStart();
}
updateScrims();
- mDurationOverride = -1;
- mAnimationDelay = 0;
- mSkipFirstFrame = false;
// Make sure that we always call the listener even if we didn't start an animation.
endAnimateKeyguardFadingOut(false /* force */);
return true;
}
+ private void onFinished() {
+ if (mWakeLockHeld) {
+ mWakeLock.release();
+ mWakeLockHeld = false;
+ }
+ if (mCallback != null) {
+ mCallback.onFinished();
+ mCallback = null;
+ }
+ // When unlocking with fingerprint, we'll fade the scrims from black to transparent.
+ // At the end of the animation we need to remove the tint.
+ if (mState == ScrimState.UNLOCKED) {
+ mCurrentInFrontTint = Color.TRANSPARENT;
+ mCurrentBehindTint = Color.TRANSPARENT;
+ }
+ }
+
private void endAnimateKeyguardFadingOut(boolean force) {
mAnimateKeyguardFadingOut = false;
if (force || (!isAnimating(mScrimInFront) && !isAnimating(mScrimBehind))) {
@@ -603,8 +551,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
mOnAnimationFinished = null;
}
mKeyguardFadingOutInProgress = false;
- if (!mWakeAndUnlocking || force)
- mAnimatingDozeUnlock = false;
}
}
@@ -641,16 +587,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
}
private void updateHeadsUpScrim(boolean animate) {
- updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha(), mCurrentHeadsUpAlpha);
+ updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha());
}
- private void updateScrim(boolean animate, View scrim, float alpha, float currentAlpha) {
- if (mKeyguardFadingOutInProgress && mKeyguardFadeoutAnimation.getCurrentPlayTime() != 0) {
- return;
- }
+ @VisibleForTesting
+ void setOnAnimationFinished(Runnable onAnimationFinished) {
+ mOnAnimationFinished = onAnimationFinished;
+ }
+
+ private void updateScrim(boolean animate, View scrim, float alpha) {
+ final float currentAlpha = scrim instanceof ScrimView ? ((ScrimView) scrim).getViewAlpha()
+ : scrim.getAlpha();
- ValueAnimator previousAnimator = ViewState.getChildTag(scrim,
- TAG_KEY_ANIM);
+ ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM);
float animEndValue = -1;
if (previousAnimator != null) {
if (animate || alpha == currentAlpha) {
@@ -664,9 +613,32 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
}
}
- if (alpha != currentAlpha && alpha != animEndValue) {
+
+ final boolean blankingInProgress = mScrimInFront.getTag(TAG_KEY_ANIM_BLANK) != null;
+ if (mBlankScreen || blankingInProgress) {
+ if (!blankingInProgress) {
+ blankDisplay();
+ }
+ return;
+ } else if (!mScreenBlankingCallbackCalled) {
+ // Not blanking the screen. Letting the callback know that we're ready
+ // to replace what was on the screen before.
+ if (mCallback != null) {
+ mCallback.onDisplayBlanked();
+ mScreenBlankingCallbackCalled = true;
+ }
+ }
+
+ final ScrimView scrimView = scrim instanceof ScrimView ? (ScrimView) scrim : null;
+ final boolean wantsAlphaUpdate = alpha != currentAlpha && alpha != animEndValue;
+ final boolean wantsTintUpdate = scrimView != null
+ && scrimView.getTint() != getCurrentScrimTint(scrimView);
+
+ if (wantsAlphaUpdate || wantsTintUpdate) {
if (animate) {
- startScrimAnimation(scrim, alpha);
+ final float fromAlpha = scrimView == null ? scrim.getAlpha()
+ : scrimView.getViewAlpha();
+ startScrimAnimation(scrim, fromAlpha, alpha);
scrim.setTag(TAG_START_ALPHA, currentAlpha);
scrim.setTag(TAG_END_ALPHA, alpha);
} else {
@@ -685,13 +657,62 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
} else {
// update the alpha directly
- setCurrentScrimAlpha(scrim, alpha);
- updateScrimColor(scrim);
+ updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
+ onFinished();
}
}
+ } else {
+ onFinished();
}
}
+ private void blankDisplay() {
+ final float initialAlpha = mScrimInFront.getViewAlpha();
+ final int initialTint = mScrimInFront.getTint();
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.addUpdateListener(animation -> {
+ final float amount = (float) animation.getAnimatedValue();
+ float animAlpha = MathUtils.lerp(initialAlpha, 1, amount);
+ int animTint = ColorUtils.blendARGB(initialTint, Color.BLACK, amount);
+ updateScrimColor(mScrimInFront, animAlpha, animTint);
+ dispatchScrimsVisible();
+ });
+ anim.setInterpolator(getInterpolator());
+ anim.setDuration(mDozeParameters.getPulseInDuration());
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCallback != null) {
+ mCallback.onDisplayBlanked();
+ mScreenBlankingCallbackCalled = true;
+ }
+ Runnable blankingCallback = () -> {
+ mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, null);
+ mBlankScreen = false;
+ // Try again.
+ updateScrims();
+ };
+
+ // Setting power states can happen after we push out the frame. Make sure we
+ // stay fully opaque until the power state request reaches the lower levels.
+ getHandler().postDelayed(blankingCallback, 100);
+
+ }
+ });
+ anim.start();
+ mScrimInFront.setTag(TAG_KEY_ANIM_BLANK, anim);
+
+ // Finish animation if we're already at its final state
+ if (initialAlpha == 1 && mScrimInFront.getTint() == Color.BLACK) {
+ anim.end();
+ }
+ }
+
+ @VisibleForTesting
+ protected Handler getHandler() {
+ return Handler.getMain();
+ }
+
/**
* Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
* the heads up is in its resting space and 1 means it's fully dragged out.
@@ -719,23 +740,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
return alpha * expandFactor;
}
- public void forceHideScrims(boolean hide, boolean animated) {
- mForceHideScrims = hide;
- mAnimateChange = animated;
- scheduleUpdate();
- }
-
- public void dontAnimateBouncerChangesUntilNextFrame() {
- mDontAnimateBouncerChanges = true;
- }
-
public void setExcludedBackgroundArea(Rect area) {
mScrimBehind.setExcludedArea(area);
}
public int getBackgroundColor() {
int color = mLockColors.getMainColor();
- return Color.argb((int) (mScrimBehind.getAlpha() * Color.alpha(color)),
+ return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)),
Color.red(color), Color.green(color), Color.blue(color));
}
@@ -764,27 +775,41 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
}
if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM,
- ColorExtractor.TYPE_DARK, mKeyguardShowing);
+ ColorExtractor.TYPE_DARK, mState != ScrimState.UNLOCKED);
mNeedsDrawableColorUpdate = true;
scheduleUpdate();
}
}
- public void dump(PrintWriter pw) {
- pw.println(" ScrimController:");
+ @VisibleForTesting
+ protected WakeLock createWakeLock() {
+ return new DelayedWakeLock(getHandler(),
+ WakeLock.createPartial(mContext, "Doze"));
+ }
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(" ScrimController:");
+ pw.print(" state:"); pw.println(mState);
pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
pw.print(" alpha="); pw.print(mCurrentInFrontAlpha);
- pw.print(" dozeAlpha="); pw.print(mDozeInFrontAlpha);
pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint()));
pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
pw.print(" alpha="); pw.print(mCurrentBehindAlpha);
- pw.print(" dozeAlpha="); pw.print(mDozeBehindAlpha);
pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint()));
- pw.print(" mBouncerShowing="); pw.println(mBouncerShowing);
pw.print(" mTracking="); pw.println(mTracking);
- pw.print(" mForceHideScrims="); pw.println(mForceHideScrims);
+ }
+
+ public interface Callback {
+ default void onStart() {
+ }
+ default void onDisplayBlanked() {
+ }
+ default void onFinished() {
+ }
+ default void onCancelled() {
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
new file mode 100644
index 000000000000..0db98f372561
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -0,0 +1,208 @@
+/*
+ * 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.statusbar.phone;
+
+import android.graphics.Color;
+import android.os.Trace;
+
+import com.android.systemui.statusbar.ScrimView;
+
+/**
+ * Possible states of the ScrimController state machine.
+ */
+public enum ScrimState {
+
+ /**
+ * Initial state.
+ */
+ UNINITIALIZED,
+
+ /**
+ * On the lock screen.
+ */
+ KEYGUARD {
+
+ @Override
+ public void prepare(ScrimState previousState) {
+ // DisplayPowerManager will blank the screen, we'll just
+ // set our scrim to black in this frame to avoid flickering and
+ // fade it out afterwards.
+ mBlankScreen = previousState == ScrimState.AOD;
+ if (previousState == ScrimState.AOD) {
+ updateScrimColor(mScrimInFront, 1, Color.BLACK);
+ }
+ mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
+ mCurrentInFrontAlpha = 0;
+ }
+ },
+
+ /**
+ * Showing password challenge.
+ */
+ BOUNCER {
+ @Override
+ public void prepare(ScrimState previousState) {
+ mCurrentBehindAlpha = ScrimController.SCRIM_BEHIND_ALPHA_UNLOCKING;
+ mCurrentInFrontAlpha = ScrimController.SCRIM_IN_FRONT_ALPHA_LOCKED;
+ }
+ },
+
+ /**
+ * Changing screen brightness from quick settings.
+ */
+ BRIGHTNESS_MIRROR {
+ @Override
+ public void prepare(ScrimState previousState) {
+ mCurrentBehindAlpha = 0;
+ mCurrentInFrontAlpha = 0;
+ }
+ },
+
+ /**
+ * Always on display or screen off.
+ */
+ AOD {
+ @Override
+ public void prepare(ScrimState previousState) {
+ if (previousState == ScrimState.PULSING) {
+ updateScrimColor(mScrimInFront, 1, Color.BLACK);
+ }
+ final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
+ mBlankScreen = previousState == ScrimState.PULSING;
+ mCurrentBehindAlpha = 1;
+ mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
+ mCurrentInFrontTint = Color.BLACK;
+ mCurrentBehindTint = Color.BLACK;
+ // DisplayPowerManager will blank the screen for us, we just need
+ // to set our state.
+ mAnimateChange = false;
+ }
+ },
+
+ /**
+ * When phone wakes up because you received a notification.
+ */
+ PULSING {
+ @Override
+ public void prepare(ScrimState previousState) {
+ mCurrentBehindAlpha = 1;
+ mCurrentInFrontAlpha = 0;
+ mCurrentInFrontTint = Color.BLACK;
+ mCurrentBehindTint = Color.BLACK;
+ mBlankScreen = true;
+ updateScrimColor(mScrimInFront, 1, Color.BLACK);
+ }
+ },
+
+ /**
+ * Unlocked on top of an app (launcher or any other activity.)
+ */
+ UNLOCKED {
+ @Override
+ public void prepare(ScrimState previousState) {
+ mCurrentBehindAlpha = 0;
+ mCurrentInFrontAlpha = 0;
+ mAnimationDuration = StatusBar.FADE_KEYGUARD_DURATION;
+
+ if (previousState == ScrimState.AOD) {
+ // Fade from black to transparent when coming directly from AOD
+ updateScrimColor(mScrimInFront, 1, Color.BLACK);
+ updateScrimColor(mScrimBehind, 1, Color.BLACK);
+ // Scrims should still be black at the end of the transition.
+ mCurrentInFrontTint = Color.BLACK;
+ mCurrentBehindTint = Color.BLACK;
+ mBlankScreen = true;
+ } else {
+ // Scrims should still be black at the end of the transition.
+ mCurrentInFrontTint = Color.TRANSPARENT;
+ mCurrentBehindTint = Color.TRANSPARENT;
+ mBlankScreen = false;
+ }
+ }
+ };
+
+ boolean mBlankScreen = false;
+ long mAnimationDuration = ScrimController.ANIMATION_DURATION;
+ int mCurrentInFrontTint = Color.TRANSPARENT;
+ int mCurrentBehindTint = Color.TRANSPARENT;
+ boolean mAnimateChange = true;
+ float mCurrentInFrontAlpha;
+ float mCurrentBehindAlpha;
+ float mAodFrontScrimAlpha;
+ float mScrimBehindAlphaKeyguard;
+ ScrimView mScrimInFront;
+ ScrimView mScrimBehind;
+ DozeParameters mDozeParameters;
+
+ public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters) {
+ mScrimInFront = scrimInFront;
+ mScrimBehind = scrimBehind;
+ mDozeParameters = dozeParameters;
+ }
+
+ public void prepare(ScrimState previousState) {
+ }
+
+ public float getFrontAlpha() {
+ return mCurrentInFrontAlpha;
+ }
+
+ public float getBehindAlpha() {
+ return mCurrentBehindAlpha;
+ }
+
+ public int getFrontTint() {
+ return mCurrentInFrontTint;
+ }
+
+ public int getBehindTint() {
+ return mCurrentBehindTint;
+ }
+
+ public long getAnimationDuration() {
+ return mAnimationDuration;
+ }
+
+ public boolean getBlanksScreen() {
+ return mBlankScreen;
+ }
+
+ public void updateScrimColor(ScrimView scrim, float alpha, int tint) {
+ Trace.traceCounter(Trace.TRACE_TAG_APP,
+ scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha",
+ (int) (alpha * 255));
+
+ Trace.traceCounter(Trace.TRACE_TAG_APP,
+ scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint",
+ Color.alpha(tint));
+
+ scrim.setTint(tint);
+ scrim.setViewAlpha(alpha);
+ }
+
+ public boolean getAnimateChange() {
+ return mAnimateChange;
+ }
+
+ public void setAodFrontScrimAlpha(float aodFrontScrimAlpha) {
+ mAodFrontScrimAlpha = aodFrontScrimAlpha;
+ }
+
+ public void setScrimBehindAlphaKeyguard(float scrimBehindAlphaKeyguard) {
+ mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
+ }
+} \ No newline at end of file
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 43cc0f771a97..a5609da78abb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -238,6 +238,7 @@ import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
.OnChildLocationsChangedListener;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.volume.VolumeComponent;
import java.io.FileDescriptor;
@@ -386,6 +387,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private VolumeComponent mVolumeComponent;
private BrightnessMirrorController mBrightnessMirrorController;
+ private boolean mBrightnessMirrorVisible;
protected FingerprintUnlockController mFingerprintUnlockController;
private LightBarController mLightBarController;
protected LockscreenWallpaper mLockscreenWallpaper;
@@ -646,6 +648,31 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
+ // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
+ // this animation is tied to the scrim for historic reasons.
+ // TODO: notify when keyguard has faded away instead of the scrim.
+ private final ScrimController.Callback mUnlockScrimCallback = new ScrimController
+ .Callback() {
+ @Override
+ public void onFinished() {
+ notifyKeyguardState();
+ }
+
+ @Override
+ public void onCancelled() {
+ notifyKeyguardState();
+ }
+
+ private void notifyKeyguardState() {
+ if (mStatusBarKeyguardViewManager == null) {
+ Log.w(TAG, "Tried to notify keyguard visibility when "
+ + "mStatusBarKeyguardViewManager was null");
+ return;
+ }
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }
+ };
+
private NotificationMessagingUtil mMessagingUtil;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private UserSwitcherController mUserSwitcherController;
@@ -1045,7 +1072,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mStatusBarWindowManager != null) {
mStatusBarWindowManager.setScrimsVisible(scrimsVisible);
}
- });
+ }, new DozeParameters(mContext));
if (mScrimSrcModeEnabled) {
Runnable runnable = () -> {
boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
@@ -1081,7 +1108,10 @@ public class StatusBar extends SystemUI implements DemoMode,
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController);
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
- mScrimController);
+ (visible) -> {
+ mBrightnessMirrorVisible = visible;
+ updateScrimController();
+ });
fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
QS qs = (QS) f;
if (qs instanceof QSFragment) {
@@ -1457,8 +1487,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mDozeScrimController, keyguardViewMediator,
mScrimController, this, UnlockMethodCache.getInstance(mContext));
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
- getBouncerContainer(), mScrimController,
- mFingerprintUnlockController);
+ getBouncerContainer(), mFingerprintUnlockController);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
@@ -2640,7 +2669,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public boolean isPulsing() {
- return mDozeScrimController.isPulsing();
+ return mDozeScrimController != null && mDozeScrimController.isPulsing();
}
@Override
@@ -3348,7 +3377,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
if (mScrimController != null) {
- mScrimController.dump(pw);
+ mScrimController.dump(fd, pw, args);
}
if (DUMPTRUCK) {
@@ -4081,7 +4110,6 @@ public class StatusBar extends SystemUI implements DemoMode,
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mLaunchTransitionFadingAway = false;
- mScrimController.forceHideScrims(false /* hide */, false /* animated */);
updateMediaMetaData(true /* metaDataChanged */, true);
}
@@ -4114,7 +4142,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (beforeFading != null) {
beforeFading.run();
}
- mScrimController.forceHideScrims(true /* hide */, false /* animated */);
+ updateScrimController();
updateMediaMetaData(false, true);
mNotificationPanel.setAlpha(1);
mStackScroller.setParentNotFullyVisible(true);
@@ -4145,6 +4173,13 @@ public class StatusBar extends SystemUI implements DemoMode,
.setStartDelay(0)
.setDuration(FADE_KEYGUARD_DURATION_PULSING)
.setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hideKeyguard();
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }
+ })
.start();
}
@@ -4152,7 +4187,6 @@ public class StatusBar extends SystemUI implements DemoMode,
* Plays the animation when an activity that was occluding Keyguard goes away.
*/
public void animateKeyguardUnoccluding() {
- mScrimController.animateKeyguardUnoccluding(500);
mNotificationPanel.setExpandedFraction(0f);
animateExpandNotificationsPanel();
}
@@ -4340,11 +4374,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mAmbientIndicationContainer.setVisibility(View.INVISIBLE);
}
}
- if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- mScrimController.setKeyguardShowing(true);
- } else {
- mScrimController.setKeyguardShowing(false);
- }
mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
updateTheme();
updateDozingState();
@@ -4352,6 +4381,7 @@ public class StatusBar extends SystemUI implements DemoMode,
updateStackScrollerState(goingToFullShade, fromShadeLocked);
updateNotifications();
checkBarModes();
+ updateScrimController();
updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
mUnlockMethodCache.isMethodSecure(),
@@ -4415,11 +4445,10 @@ public class StatusBar extends SystemUI implements DemoMode,
boolean animate = !mDozing && mDozeServiceHost.shouldAnimateWakeup();
mNotificationPanel.setDozing(mDozing, animate);
mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
- mScrimController.setDozing(mDozing);
+ mDozeScrimController.setDozing(mDozing);
mKeyguardIndicationController.setDozing(mDozing);
mNotificationPanel.setDark(mDozing, animate);
updateQsExpansionEnabled();
- mDozeScrimController.setDozing(mDozing, animate);
updateRowStates();
Trace.endSection();
}
@@ -4914,6 +4943,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
updateHideIconsForBouncer(true /* animate */);
recomputeDisableFlags(true /* animate */);
+ updateScrimController();
}
public void cancelCurrentTouch() {
@@ -4965,12 +4995,10 @@ public class StatusBar extends SystemUI implements DemoMode,
mStackScroller.setAnimationsEnabled(true);
mVisualStabilityManager.setScreenOn(true);
mNotificationPanel.setTouchDisabled(false);
-
- maybePrepareWakeUpFromAod();
-
mDozeServiceHost.stopDozing();
updateVisibleToUser();
updateIsKeyguard();
+ updateScrimController();
}
};
@@ -4980,18 +5008,16 @@ public class StatusBar extends SystemUI implements DemoMode,
mFalsingManager.onScreenTurningOn();
mNotificationPanel.onScreenTurningOn();
- maybePrepareWakeUpFromAod();
-
if (mLaunchCameraOnScreenTurningOn) {
mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
mLaunchCameraOnScreenTurningOn = false;
}
+
+ updateScrimController();
}
@Override
public void onScreenTurnedOn() {
- mScrimController.wakeUpFromAod();
- mDozeScrimController.onScreenTurnedOn();
}
@Override
@@ -5009,13 +5035,6 @@ public class StatusBar extends SystemUI implements DemoMode,
return mWakefulnessLifecycle.getWakefulness();
}
- private void maybePrepareWakeUpFromAod() {
- int wakefulness = mWakefulnessLifecycle.getWakefulness();
- if (mDozing && wakefulness == WAKEFULNESS_WAKING && !isPulsing()) {
- mScrimController.prepareWakeUpFromAod();
- }
- }
-
private void vibrateForCameraGesture() {
// Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
mVibrator.vibrate(mCameraLaunchGestureVibePattern, -1 /* repeat */);
@@ -5098,12 +5117,12 @@ public class StatusBar extends SystemUI implements DemoMode,
if (!mDeviceInteractive) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
// comes on.
- mScrimController.dontAnimateBouncerChangesUntilNextFrame();
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (isScreenTurningOnOrOn()) {
if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
+ updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
@@ -5145,15 +5164,16 @@ public class StatusBar extends SystemUI implements DemoMode,
private void updateDozing() {
Trace.beginSection("StatusBar#updateDozing");
// When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
- mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
+ boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
|| mFingerprintUnlockController.getMode()
== FingerprintUnlockController.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 (mFingerprintUnlockController.getMode() ==
FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
- mDozing = false;
+ dozing = false;
}
+ mDozing = dozing;
mStatusBarWindowManager.setDozing(mDozing);
mStatusBarKeyguardViewManager.setDozing(mDozing);
if (mAmbientIndicationContainer instanceof DozeReceiver) {
@@ -5163,6 +5183,24 @@ public class StatusBar extends SystemUI implements DemoMode,
Trace.endSection();
}
+ public void updateScrimController() {
+ if (mBouncerShowing) {
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+ } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
+ mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
+ } else if (mBrightnessMirrorVisible) {
+ mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+ } else if (isPulsing()) {
+ // Handled in DozeScrimController#setPulsing
+ } else if (mDozing) {
+ mScrimController.transitionTo(ScrimState.AOD);
+ } else if (mIsKeyguard) {
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ } else {
+ mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
+ }
+ }
+
public boolean isKeyguardShowing() {
if (mStatusBarKeyguardViewManager == null) {
Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
@@ -5222,7 +5260,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
mDozeScrimController.pulse(new PulseCallback() {
-
@Override
public void onPulseStarted() {
callback.onPulseStarted();
@@ -5307,11 +5344,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
@Override
- public void abortPulsing() {
- mDozeScrimController.abortPulsing();
- }
-
- @Override
public void extendPulse() {
mDozeScrimController.extendPulse();
}
@@ -5347,7 +5379,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void setAodDimmingScrim(float scrimOpacity) {
- mDozeScrimController.setAodDimmingScrim(scrimOpacity);
+ ScrimState.AOD.setAodFrontScrimAlpha(scrimOpacity);
}
public void dispatchDoubleTap(float viewX, float viewY) {
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 bcde556a5954..ef05bbb4fe3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -24,7 +24,6 @@ import android.content.ComponentCallbacks2;
import android.content.Context;
import android.os.Bundle;
import android.os.SystemClock;
-import android.os.Trace;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -71,17 +70,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
protected final Context mContext;
private final StatusBarWindowManager mStatusBarWindowManager;
- private final boolean mDisplayBlanksAfterDoze;
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
protected StatusBar mStatusBar;
- private ScrimController mScrimController;
private FingerprintUnlockController mFingerprintUnlockController;
private ViewGroup mContainer;
- private boolean mScreenTurnedOn;
protected KeyguardBouncer mBouncer;
protected boolean mShowing;
protected boolean mOccluded;
@@ -95,12 +91,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean mLastBouncerDismissible;
protected boolean mLastRemoteInputActive;
private boolean mLastDozing;
- private boolean mLastDeferScrimFadeOut;
private int mLastFpMode;
private OnDismissAction mAfterKeyguardGoneAction;
private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
- private boolean mDeferScrimFadeOut;
// Dismiss action to be launched when we stop dozing or the keyguard is gone.
private DismissWithActionRequest mPendingWakeupAction;
@@ -125,18 +119,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mLockPatternUtils = lockPatternUtils;
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
- mDisplayBlanksAfterDoze = context.getResources().getBoolean(
- com.android.internal.R.bool.config_displayBlanksAfterDoze);
}
public void registerStatusBar(StatusBar statusBar,
ViewGroup container,
- ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController,
DismissCallbackRegistry dismissCallbackRegistry) {
mStatusBar = statusBar;
mContainer = container;
- mScrimController = scrimController;
mFingerprintUnlockController = fingerprintUnlockController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry);
@@ -149,7 +139,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void show(Bundle options) {
mShowing = true;
mStatusBarWindowManager.setKeyguardShowing(true);
- mScrimController.abortKeyguardFadingOut();
reset(true /* hideBouncerWhenShowing */);
}
@@ -253,15 +242,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
public void onScreenTurnedOn() {
- Trace.beginSection("StatusBarKeyguardViewManager#onScreenTurnedOn");
- mScreenTurnedOn = true;
- if (mDeferScrimFadeOut) {
- mDeferScrimFadeOut = false;
- animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
- true /* skipFirstFrame */);
- updateStates();
- }
- Trace.endSection();
+ // TODO: remove
}
@Override
@@ -285,7 +266,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
public void onScreenTurnedOff() {
- mScreenTurnedOn = false;
+ // TODO: remove
}
public void notifyDeviceWakeUpRequested() {
@@ -374,10 +355,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mStatusBarWindowManager.setKeyguardFadingAway(true);
hideBouncer(true /* destroyView */);
updateStates();
- mScrimController.animateKeyguardFadingOut(
- StatusBar.FADE_KEYGUARD_START_DELAY,
- StatusBar.FADE_KEYGUARD_DURATION, null,
- false /* skipFirstFrame */);
}
}, new Runnable() {
@Override
@@ -400,36 +377,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mFingerprintUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
- mStatusBarWindowManager.setKeyguardFadingAway(true);
mStatusBar.fadeKeyguardWhilePulsing();
- animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
- mStatusBar::hideKeyguard, false /* skipFirstFrame */);
+ wakeAndUnlockDejank();
} else {
mFingerprintUnlockController.startKeyguardFadingAway();
mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowManager.setKeyguardFadingAway(true);
- if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK) {
- boolean turnedOnSinceAuth =
- mFingerprintUnlockController.hasScreenTurnedOnSinceAuthenticating();
- if (!mScreenTurnedOn || mDisplayBlanksAfterDoze && !turnedOnSinceAuth) {
- // Not ready to animate yet; either because the screen is not on yet,
- // or it is on but will turn off before waking out of doze.
- mDeferScrimFadeOut = true;
- } else {
-
- // Screen is already on, don't defer with fading out.
- animateScrimControllerKeyguardFadingOut(0,
- WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
- true /* skipFirstFrame */);
- }
- } else {
- animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
- false /* skipFirstFrame */);
- }
+ wakeAndUnlockDejank();
} else {
- mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
mStatusBar.finishKeyguardFadingAway();
mFingerprintUnlockController.finishKeyguardFadingAway();
}
@@ -449,30 +406,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncer.prepare();
}
- private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
- boolean skipFirstFrame) {
- animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */,
- skipFirstFrame);
+ public void onKeyguardFadedAway() {
+ mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
+ 100);
+ mStatusBar.finishKeyguardFadingAway();
+ mFingerprintUnlockController.finishKeyguardFadingAway();
+ WindowManagerGlobal.getInstance().trimMemory(
+ ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+
}
- private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
- final Runnable endRunnable, boolean skipFirstFrame) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0);
- mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() {
- @Override
- public void run() {
- if (endRunnable != null) {
- endRunnable.run();
- }
- mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
- 100);
- mStatusBar.finishKeyguardFadingAway();
- mFingerprintUnlockController.finishKeyguardFadingAway();
- WindowManagerGlobal.getInstance().trimMemory(
- ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0);
- }
- }, skipFirstFrame);
+ private void wakeAndUnlockDejank() {
if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
&& LatencyTracker.isEnabled(mContext)) {
DejankUtils.postAfterTraversal(() ->
@@ -593,7 +537,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
mStatusBar.setBouncerShowing(bouncerShowing);
- mScrimController.setBouncerShowing(bouncerShowing);
}
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
@@ -611,7 +554,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mLastBouncerDismissible = bouncerDismissible;
mLastRemoteInputActive = remoteInputActive;
mLastDozing = mDozing;
- mLastDeferScrimFadeOut = mDeferScrimFadeOut;
mLastFpMode = mFingerprintUnlockController.getMode();
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
@@ -624,7 +566,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
boolean keyguardShowing = mShowing && !mOccluded;
boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING;
return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
- || mRemoteInputActive) && !mDeferScrimFadeOut;
+ || mRemoteInputActive);
}
/**
@@ -634,7 +576,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
boolean keyguardShowing = mLastShowing && !mLastOccluded;
boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING;
return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
- || mLastRemoteInputActive) && !mLastDeferScrimFadeOut;
+ || mLastRemoteInputActive);
}
public boolean shouldDismissOnMenuPressed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index b0553d72dd49..a011952f1476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.NonNull;
import android.util.ArraySet;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -26,46 +27,47 @@ import android.widget.FrameLayout;
import com.android.internal.util.Preconditions;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import java.util.function.Consumer;
+
/**
* Controls showing and hiding of the brightness mirror.
*/
public class BrightnessMirrorController
implements CallbackController<BrightnessMirrorController.BrightnessMirrorListener> {
- private final NotificationStackScrollLayout mStackScroller;
- public long TRANSITION_DURATION_OUT = 150;
- public long TRANSITION_DURATION_IN = 200;
+ private final static long TRANSITION_DURATION_OUT = 150;
+ private final static long TRANSITION_DURATION_IN = 200;
private final StatusBarWindowView mStatusBarWindow;
- private final ScrimController mScrimController;
+ private final NotificationStackScrollLayout mStackScroller;
+ private final Consumer<Boolean> mVisibilityCallback;
private final View mNotificationPanel;
private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>();
private final int[] mInt2Cache = new int[2];
private View mBrightnessMirror;
public BrightnessMirrorController(StatusBarWindowView statusBarWindow,
- ScrimController scrimController) {
+ @NonNull Consumer<Boolean> visibilityCallback) {
mStatusBarWindow = statusBarWindow;
mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
mStackScroller = statusBarWindow.findViewById(R.id.notification_stack_scroller);
- mScrimController = scrimController;
+ mVisibilityCallback = visibilityCallback;
}
public void showMirror() {
mBrightnessMirror.setVisibility(View.VISIBLE);
mStackScroller.setFadingOut(true);
- mScrimController.forceHideScrims(true /* hide */, true /* animated */);
+ mVisibilityCallback.accept(true);
outAnimation(mNotificationPanel.animate())
.withLayer();
}
public void hideMirror() {
- mScrimController.forceHideScrims(false /* hide */, true /* animated */);
+ mVisibilityCallback.accept(false);
inAnimation(mNotificationPanel.animate())
.withLayer()
.withEndAction(() -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index b0c9f32873ff..1c104cffc3c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -19,12 +19,13 @@ package com.android.systemui.doze;
import android.annotation.NonNull;
import android.app.PendingIntent;
+import com.android.systemui.util.wakelock.WakeLock;
+
/**
* A rudimentary fake for DozeHost.
*/
class DozeHostFake implements DozeHost {
Callback callback;
- boolean pulseAborted;
boolean pulseExtended;
boolean animateWakeup;
boolean dozing;
@@ -92,11 +93,6 @@ class DozeHostFake implements DozeHost {
}
@Override
- public void abortPulsing() {
- pulseAborted = true;
- }
-
- @Override
public void extendPulse() {
pulseExtended = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 004ff29f7eac..1c9c7949a971 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -49,6 +49,7 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
@@ -108,6 +109,7 @@ public class QSTileImplTest extends SysuiTestCase {
}
@Test
+ @Ignore("flaky")
public void testStaleTimeout() throws InterruptedException {
when(mTile.getStaleTimeout()).thenReturn(5l);
clearInvocations(mTile);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
index 4c3bf1081792..a4bcc69c0e10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
@@ -86,9 +86,9 @@ public class ScrimViewTest extends LeakCheckedTest {
@Test
public void testSetViewAlpha_propagatesToDrawable() {
- float alpha = 0.5f;
+ final float alpha = 0.5f;
mView.setViewAlpha(alpha);
- assertEquals(mView.getViewAlpha(), alpha);
+ assertEquals("View alpha did not propagate to drawable", alpha, mView.getViewAlpha());
}
@Test
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
new file mode 100644
index 000000000000..ca2f713d2b6f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.statusbar.phone;
+
+import static org.mockito.ArgumentMatchers.any;
+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.verify;
+
+import android.os.Debug;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.DozeHost;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class DozeScrimControllerTest extends SysuiTestCase {
+
+ private ScrimController mScrimController;
+ private DozeScrimController mDozeScrimController;
+
+ @Before
+ public void setup() {
+ mScrimController = mock(ScrimController.class);
+ // Make sure callbacks will be invoked to complete the lifecycle.
+ doAnswer(invocationOnMock -> {
+ ScrimController.Callback callback = invocationOnMock.getArgument(1);
+ callback.onStart();
+ callback.onDisplayBlanked();
+ callback.onFinished();
+ return null;
+ }).when(mScrimController).transitionTo(any(ScrimState.class),
+ any(ScrimController.Callback.class));
+
+ mDozeScrimController = new DozeScrimController(mScrimController, getContext());
+ mDozeScrimController.setDozing(true);
+ }
+
+ @Test
+ public void changesScrimControllerState() {
+ mDozeScrimController.pulse(mock(DozeHost.PulseCallback.class), 0);
+ verify(mScrimController).transitionTo(eq(ScrimState.PULSING),
+ any(ScrimController.Callback.class));
+ }
+
+ @Test
+ public void callsPulseCallback() {
+ DozeHost.PulseCallback callback = mock(DozeHost.PulseCallback.class);
+ mDozeScrimController.pulse(callback, 0);
+
+ verify(callback).onPulseStarted();
+ mDozeScrimController.pulseOutNow();
+ verify(callback).onPulseFinished();
+ }
+
+ @Test
+ public void secondPulseIsSuppressed() {
+ DozeHost.PulseCallback callback1 = mock(DozeHost.PulseCallback.class);
+ DozeHost.PulseCallback callback2 = mock(DozeHost.PulseCallback.class);
+ mDozeScrimController.pulse(callback1, 0);
+ mDozeScrimController.pulse(callback2, 0);
+
+ verify(callback1, never()).onPulseFinished();
+ verify(callback2).onPulseFinished();
+ }
+
+ @Test
+ public void suppressesPulseIfNotDozing() {
+ mDozeScrimController.setDozing(false);
+ DozeHost.PulseCallback callback = mock(DozeHost.PulseCallback.class);
+ mDozeScrimController.pulse(callback, 0);
+
+ verify(callback).onPulseFinished();
+ }
+}
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
new file mode 100644
index 000000000000..b9f695be90cf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -0,0 +1,300 @@
+/*
+ * 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.statusbar.phone;
+
+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.animation.Animator;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.util.wakelock.WakeLock;
+import com.android.systemui.utils.os.FakeHandler;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class ScrimControllerTest extends SysuiTestCase {
+
+ private SynchronousScrimController mScrimController;
+ private ScrimView mScrimBehind;
+ private ScrimView mScrimInFront;
+ private View mHeadsUpScrim;
+ private Consumer<Boolean> mScrimVisibilityCallback;
+ private Boolean mScrimVisibile;
+ private LightBarController mLightBarController;
+ private DozeParameters mDozeParamenters;
+ private WakeLock mWakeLock;
+ private boolean mAlwaysOnEnabled;
+
+ @Before
+ public void setup() {
+ mLightBarController = mock(LightBarController.class);
+ mScrimBehind = new ScrimView(getContext());
+ mScrimInFront = new ScrimView(getContext());
+ mHeadsUpScrim = mock(View.class);
+ mWakeLock = mock(WakeLock.class);
+ mAlwaysOnEnabled = true;
+ mScrimVisibilityCallback = (Boolean visible) -> mScrimVisibile = visible;
+ mDozeParamenters = mock(DozeParameters.class);
+ when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
+ mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
+ mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters);
+ }
+
+ @Test
+ public void initialState() {
+ Assert.assertEquals("ScrimController should start initialized",
+ mScrimController.getState(), ScrimState.UNINITIALIZED);
+ }
+
+ @Test
+ public void transitionToKeyguard() {
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible without tint
+ assertScrimVisibility(false /* front */, true /* behind */);
+ assertScrimTint(mScrimBehind, false /* tinted */);
+ }
+
+ @Test
+ public void transitionToAod() {
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible with tint
+ assertScrimVisibility(false /* front */, true /* behind */);
+ assertScrimTint(mScrimBehind, true /* tinted */);
+ assertScrimTint(mScrimInFront, true /* tinted */);
+ }
+
+ @Test
+ public void transitionToPulsing() {
+ mScrimController.transitionTo(ScrimState.PULSING);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible with tint
+ // Pulse callback should have been invoked
+ assertScrimVisibility(false /* front */, true /* behind */);
+ assertScrimTint(mScrimBehind, true /* tinted */);
+ }
+
+ @Test
+ public void transitionToBouncer() {
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible without tint
+ assertScrimVisibility(true /* front */, true /* behind */);
+ assertScrimTint(mScrimBehind, false /* tinted */);
+ }
+
+ @Test
+ public void transitionToUnlocked() {
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be transparent
+ assertScrimVisibility(false /* front */, false /* behind */);
+ assertScrimTint(mScrimBehind, false /* tinted */);
+ assertScrimTint(mScrimInFront, false /* tinted */);
+
+ // Back scrim should be visible after start dragging
+ mScrimController.setPanelExpansion(0.5f);
+ assertScrimVisibility(false /* front */, true /* behind */);
+ }
+
+ @Test
+ public void transitionToUnlockedFromAod() {
+ // Simulate unlock with fingerprint
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ // Immediately tinted after the transition starts
+ assertScrimTint(mScrimInFront, true /* tinted */);
+ assertScrimTint(mScrimBehind, true /* tinted */);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be transparent
+ // Neither scrims should be tinted anymore after the animation.
+ assertScrimVisibility(false /* front */, false /* behind */);
+ assertScrimTint(mScrimInFront, false /* tinted */);
+ assertScrimTint(mScrimBehind, false /* tinted */);
+ }
+
+ @Test
+ public void scrimBlanksBeforeLeavingAoD() {
+ // Simulate unlock with fingerprint
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ mScrimController.transitionTo(ScrimState.UNLOCKED,
+ new ScrimController.Callback() {
+ @Override
+ public void onDisplayBlanked() {
+ // Front scrim should be black in the middle of the transition
+ Assert.assertTrue("Scrim should be visible during transition. Alpha: "
+ + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
+ assertScrimTint(mScrimInFront, true /* tinted */);
+ Assert.assertTrue("Scrim should be visible during transition.",
+ mScrimVisibile);
+ }
+ });
+ mScrimController.finishAnimationsImmediately();
+ }
+
+ @Test
+ public void testScrimCallback() {
+ int[] callOrder = {0, 0, 0};
+ int[] currentCall = {0};
+ mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
+ @Override
+ public void onStart() {
+ callOrder[0] = ++currentCall[0];
+ }
+
+ @Override
+ public void onDisplayBlanked() {
+ callOrder[1] = ++currentCall[0];
+ }
+
+ @Override
+ public void onFinished() {
+ callOrder[2] = ++currentCall[0];
+ }
+ });
+ mScrimController.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]);
+ }
+
+ @Test
+ public void testScrimCallbacksWithoutAmbientDisplay() {
+ mAlwaysOnEnabled = false;
+ testScrimCallback();
+ }
+
+ @Test
+ public void testScrimCallbackCancelled() {
+ boolean[] cancelledCalled = {false};
+ mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
+ @Override
+ public void onCancelled() {
+ cancelledCalled[0] = true;
+ }
+ });
+ mScrimController.transitionTo(ScrimState.PULSING);
+ Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
+ }
+
+ @Test
+ public void testHoldsWakeLock() {
+ mScrimController.transitionTo(ScrimState.AOD);
+ verify(mWakeLock, times(1)).acquire();
+ verify(mWakeLock, never()).release();
+ mScrimController.finishAnimationsImmediately();
+ verify(mWakeLock, times(1)).release();
+ }
+
+ private void assertScrimTint(ScrimView scrimView, boolean tinted) {
+ final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
+ final String name = scrimView == mScrimInFront ? "front" : "back";
+ Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
+ +" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()),
+ tinted, viewIsTinted);
+ }
+
+ private void assertScrimVisibility(boolean inFront, boolean behind) {
+ Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
+ + mScrimInFront.getViewAlpha(), inFront, mScrimInFront.getViewAlpha() > 0);
+ Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
+ + mScrimBehind.getViewAlpha(), behind, mScrimBehind.getViewAlpha() > 0);
+ Assert.assertEquals("Invalid visibility.", inFront || behind, mScrimVisibile);
+ }
+
+ /**
+ * Special version of ScrimController where animations have 0 duration for test purposes.
+ */
+ private class SynchronousScrimController extends ScrimController {
+
+ private FakeHandler mHandler;
+
+ public SynchronousScrimController(LightBarController lightBarController,
+ ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
+ Consumer<Boolean> scrimVisibleListener, DozeParameters dozeParameters) {
+ super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
+ scrimVisibleListener, dozeParameters);
+ mHandler = new FakeHandler(Looper.myLooper());
+ }
+
+ public void finishAnimationsImmediately() {
+ boolean[] animationFinished = {false};
+ setOnAnimationFinished(()-> animationFinished[0] = true);
+
+ // Execute code that will trigger animations.
+ onPreDraw();
+
+ // Force finish screen blanking.
+ endAnimation(mScrimInFront, TAG_KEY_ANIM_BLANK);
+ mHandler.dispatchQueuedMessages();
+ // Force finish all animations.
+ endAnimation(mScrimBehind, TAG_KEY_ANIM);
+ endAnimation(mScrimInFront, TAG_KEY_ANIM);
+
+ if (!animationFinished[0]) {
+ throw new IllegalStateException("Animation never finished");
+ }
+ }
+
+ private void endAnimation(ScrimView scrimView, int tag) {
+ Animator animator = (Animator) scrimView.getTag(tag);
+ if (animator != null) {
+ animator.end();
+ }
+ }
+
+ @Override
+ protected Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ protected WakeLock createWakeLock() {
+ return mWakeLock;
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 4330f5d892c7..ca1524959358 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -48,6 +48,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
@@ -3051,6 +3052,11 @@ class AlarmManagerService extends SystemService {
for (int i=0; i<triggerList.size(); i++) {
Alarm alarm = triggerList.get(i);
final boolean allowWhileIdle = (alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
+ if (alarm.wakeup) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "Dispatch wakeup alarm to " + alarm.packageName);
+ } else {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "Dispatch non-wakeup alarm to " + alarm.packageName);
+ }
try {
if (localLOGV) {
Slog.v(TAG, "sending alarm " + alarm);
@@ -3070,6 +3076,7 @@ class AlarmManagerService extends SystemService {
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index 1ad14047adf9..2c24798204eb 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -69,13 +69,10 @@ public class NetworkTimeUpdateService extends Binder {
private static final String ACTION_POLL =
"com.android.server.NetworkTimeUpdateService.action.POLL";
- private static final int NETWORK_CHANGE_EVENT_DELAY_MS = 1000;
- private static int POLL_REQUEST = 0;
+ private static final int POLL_REQUEST = 0;
private static final long NOT_SET = -1;
private long mNitzTimeSetTime = NOT_SET;
- // TODO: Have a way to look up the timezone we are in
- private long mNitzZoneSetTime = NOT_SET;
private Network mDefaultNetwork = null;
private Context mContext;
@@ -144,7 +141,6 @@ public class NetworkTimeUpdateService extends Binder {
private void registerForTelephonyIntents() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
- intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
mContext.registerReceiver(mNitzReceiver, intentFilter);
}
@@ -257,8 +253,6 @@ public class NetworkTimeUpdateService extends Binder {
if (DBG) Log.d(TAG, "Received " + action);
if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
mNitzTimeSetTime = SystemClock.elapsedRealtime();
- } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
- mNitzZoneSetTime = SystemClock.elapsedRealtime();
}
}
};
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3c955eb0f0db..66b3adb68ddd 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2192,6 +2192,11 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
+ if (StorageManager.isFileEncryptedNativeOnly()) {
+ // Not supported on FBE devices
+ return -1;
+ }
+
if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
password = "";
} else if (TextUtils.isEmpty(password)) {
@@ -2268,6 +2273,11 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
+ if (StorageManager.isFileEncryptedNativeOnly()) {
+ // Not supported on FBE devices
+ return;
+ }
+
try {
mVold.fdeSetField(field, contents);
return;
@@ -2287,6 +2297,11 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
+ if (StorageManager.isFileEncryptedNativeOnly()) {
+ // Not supported on FBE devices
+ return null;
+ }
+
try {
return mVold.fdeGetField(field);
} catch (Exception e) {
@@ -2568,7 +2583,7 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
}
@Override
- public int mkdirs(String callingPkg, String appPath) {
+ public void mkdirs(String callingPkg, String appPath) {
final int userId = UserHandle.getUserId(Binder.getCallingUid());
final UserEnvironment userEnv = new UserEnvironment(userId);
@@ -2581,8 +2596,7 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
try {
appFile = new File(appPath).getCanonicalFile();
} catch (IOException e) {
- Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
- return -1;
+ throw new IllegalStateException("Failed to resolve " + appPath + ": " + e);
}
// Try translating the app path into a vold path, but require that it
@@ -2597,9 +2611,8 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
try {
mVold.mkdirs(appPath);
- return 0;
} catch (Exception e) {
- Slog.wtf(TAG, e);
+ throw new IllegalStateException("Failed to prepare " + appPath + ": " + e);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3c8f112cfab0..b2681935798a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -246,6 +246,7 @@ import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.IBackupManager;
+import android.app.servertransaction.ConfigurationChangeItem;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
@@ -317,6 +318,7 @@ import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -631,6 +633,8 @@ public class ActivityManagerService extends IActivityManager.Stub
final ActivityStarter mActivityStarter;
+ final ClientLifecycleManager mLifecycleManager;
+
final TaskChangeNotificationController mTaskChangeNotificationController;
final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
@@ -1778,6 +1782,11 @@ public class ActivityManagerService extends IActivityManager.Stub
final boolean mPermissionReviewRequired;
+ /**
+ * Whether to force background check on all apps (for battery saver) or not.
+ */
+ boolean mForceBackgroundCheck;
+
private static String sTheRealBuildSerial = Build.UNKNOWN;
/**
@@ -2693,6 +2702,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mUserController = null;
mVrController = null;
mLockTaskController = null;
+ mLifecycleManager = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2795,6 +2805,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mRecentTasks = createRecentTasks();
mStackSupervisor.setRecentTasks(mRecentTasks);
mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
+ mLifecycleManager = new ClientLifecycleManager();
mProcessCpuThread = new Thread("CpuTracker") {
@Override
@@ -5128,7 +5139,8 @@ public class ActivityManagerService extends IActivityManager.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, finishWithRootActivity);
+ res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+ finishWithRootActivity, "finish-activity");
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
}
@@ -8614,6 +8626,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
switch (appop) {
case AppOpsManager.MODE_ALLOWED:
+ // If force-background-check is enabled, restrict all apps that aren't whitelisted.
+ if (mForceBackgroundCheck &&
+ UserHandle.isApp(uid) &&
+ !isOnDeviceIdleWhitelistLocked(uid)) {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Force background check: " +
+ uid + "/" + packageName + " restricted");
+ }
+ return ActivityManager.APP_START_MODE_DELAYED;
+ }
return ActivityManager.APP_START_MODE_NORMAL;
case AppOpsManager.MODE_IGNORED:
return ActivityManager.APP_START_MODE_DELAYED;
@@ -8713,6 +8735,9 @@ public class ActivityManagerService extends IActivityManager.Stub
return ActivityManager.APP_START_MODE_NORMAL;
}
+ /**
+ * @return whether a UID is in the system, user or temp doze whitelist.
+ */
boolean isOnDeviceIdleWhitelistLocked(int uid) {
final int appId = UserHandle.getAppId(uid);
return Arrays.binarySearch(mDeviceIdleWhitelist, appId) >= 0
@@ -10306,7 +10331,8 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
- return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
+ return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
+ "remove-task");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -14169,6 +14195,16 @@ public class ActivityManagerService extends IActivityManager.Stub
readGrantedUriPermissionsLocked();
}
+ final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+ if (pmi != null) {
+ pmi.registerLowPowerModeObserver(ServiceType.FORCE_BACKGROUND_CHECK,
+ state -> updateForceBackgroundCheck(state.batterySaverEnabled));
+ updateForceBackgroundCheck(
+ pmi.getLowPowerState(ServiceType.FORCE_BACKGROUND_CHECK).batterySaverEnabled);
+ } else {
+ Slog.wtf(TAG, "PowerManagerInternal not found.");
+ }
+
if (goingCallback != null) goingCallback.run();
traceLog.traceBegin("ActivityManagerStartApps");
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
@@ -14267,6 +14303,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ private void updateForceBackgroundCheck(boolean enabled) {
+ synchronized (this) {
+ if (mForceBackgroundCheck != enabled) {
+ mForceBackgroundCheck = enabled;
+
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Force background check " + (enabled ? "enabled" : "disabled"));
+ }
+
+ if (mForceBackgroundCheck) {
+ // Stop background services for idle UIDs.
+ doStopUidForIdleUidsLocked();
+ }
+ }
+ }
+ }
+
void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) {
synchronized (this) {
mAppErrors.killAppAtUserRequestLocked(app, fromDialog);
@@ -15618,7 +15671,6 @@ public class ActivityManagerService extends IActivityManager.Stub
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
boolean needSep = false;
- boolean printedAnything = false;
int numPers = 0;
pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
@@ -15636,7 +15688,6 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!needSep) {
pw.println(" All known processes:");
needSep = true;
- printedAnything = true;
}
pw.print(r.persistent ? " *PERS*" : " *APP*");
pw.print(" UID "); pw.print(procs.keyAt(ia));
@@ -15661,7 +15712,6 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println();
}
pw.println(" Isolated process list (sorted by uid):");
- printedAnything = true;
printed = true;
needSep = true;
}
@@ -15683,7 +15733,6 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println();
}
pw.println(" Active instrumentation:");
- printedAnything = true;
printed = true;
needSep = true;
}
@@ -15695,14 +15744,14 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mActiveUids.size() > 0) {
if (dumpUids(pw, dumpPackage, dumpAppId, mActiveUids, "UID states:", needSep)) {
- printedAnything = needSep = true;
+ needSep = true;
}
}
if (dumpAll) {
if (mValidateUids.size() > 0) {
if (dumpUids(pw, dumpPackage, dumpAppId, mValidateUids, "UID validation:",
needSep)) {
- printedAnything = needSep = true;
+ needSep = true;
}
}
}
@@ -15719,7 +15768,6 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println("):");
dumpProcessOomList(pw, this, mLruProcesses, " ", "Proc", "PERS", false, dumpPackage);
needSep = true;
- printedAnything = true;
}
if (dumpAll || dumpPackage != null) {
@@ -15735,7 +15783,6 @@ public class ActivityManagerService extends IActivityManager.Stub
needSep = true;
pw.println(" PID mappings:");
printed = true;
- printedAnything = true;
}
pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i));
pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
@@ -15758,7 +15805,6 @@ public class ActivityManagerService extends IActivityManager.Stub
needSep = true;
pw.println(" Foreground Processes:");
printed = true;
- printedAnything = true;
}
pw.print(" PID #"); pw.print(mImportantProcesses.keyAt(i));
pw.print(": "); pw.println(mImportantProcesses.valueAt(i));
@@ -15769,7 +15815,6 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mPersistentStartingProcesses.size() > 0) {
if (needSep) pw.println();
needSep = true;
- printedAnything = true;
pw.println(" Persisent processes that are starting:");
dumpProcessList(pw, this, mPersistentStartingProcesses, " ",
"Starting Norm", "Restarting PERS", dumpPackage);
@@ -15778,7 +15823,6 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mRemovedProcesses.size() > 0) {
if (needSep) pw.println();
needSep = true;
- printedAnything = true;
pw.println(" Processes that are being removed:");
dumpProcessList(pw, this, mRemovedProcesses, " ",
"Removed Norm", "Removed PERS", dumpPackage);
@@ -15787,7 +15831,6 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mProcessesOnHold.size() > 0) {
if (needSep) pw.println();
needSep = true;
- printedAnything = true;
pw.println(" Processes that are on old until the system is ready:");
dumpProcessList(pw, this, mProcessesOnHold, " ",
"OnHold Norm", "OnHold PERS", dumpPackage);
@@ -15796,9 +15839,6 @@ public class ActivityManagerService extends IActivityManager.Stub
needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage);
needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
- if (needSep) {
- printedAnything = true;
- }
if (dumpPackage == null) {
pw.println();
@@ -16070,10 +16110,7 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println();
}
}
-
- if (!printedAnything) {
- pw.println(" (nothing)");
- }
+ pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck);
}
boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -20562,9 +20599,11 @@ public class ActivityManagerService extends IActivityManager.Stub
if (app.thread != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
+ app.processName + " new config " + configCopy);
- app.thread.scheduleConfigurationChanged(configCopy);
+ mLifecycleManager.scheduleTransaction(app.thread,
+ new ConfigurationChangeItem(configCopy));
}
} catch (Exception e) {
+ Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
}
}
@@ -23332,6 +23371,24 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /**
+ * Call {@link #doStopUidLocked} (which will also stop background services) for all idle UIDs.
+ */
+ void doStopUidForIdleUidsLocked() {
+ final int size = mActiveUids.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = mActiveUids.keyAt(i);
+ if (!UserHandle.isApp(uid)) {
+ continue;
+ }
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (!uidRec.idle) {
+ continue;
+ }
+ doStopUidLocked(uidRec.uid, uidRec);
+ }
+ }
+
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
@@ -24330,6 +24387,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
}
+
+ @Override
+ public boolean isRuntimeRestarted() {
+ return mSystemServiceManager.isRuntimeRestarted();
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 11590d6ca83b..883019ea7b10 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -131,6 +131,12 @@ import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.servertransaction.MoveToDisplayItem;
+import android.app.servertransaction.MultiWindowModeChangeItem;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PipModeChangeItem;
+import android.app.servertransaction.WindowVisibilityItem;
+import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -611,8 +617,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
"Reporting activity moved to display" + ", activityRecord=" + this
+ ", displayId=" + displayId + ", config=" + config);
- app.thread.scheduleActivityMovedToDisplay(appToken, displayId,
- new Configuration(config));
+ service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+ new MoveToDisplayItem(displayId, config));
} catch (RemoteException e) {
// If process died, whatever.
}
@@ -629,7 +635,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
+ config);
- app.thread.scheduleActivityConfigurationChanged(appToken, new Configuration(config));
+ service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+ new ActivityConfigurationChangeItem(config));
} catch (RemoteException e) {
// If process died, whatever.
}
@@ -650,8 +657,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
try {
- app.thread.scheduleMultiWindowModeChanged(appToken, mLastReportedMultiWindowMode,
- overrideConfig);
+ service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+ new MultiWindowModeChangeItem(mLastReportedMultiWindowMode,
+ overrideConfig));
} catch (Exception e) {
// If process died, I don't care.
}
@@ -677,8 +685,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
- app.thread.schedulePictureInPictureModeChanged(appToken,
- mLastReportedPictureInPictureMode, overrideConfig);
+ service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+ new PipModeChangeItem(mLastReportedPictureInPictureMode,
+ overrideConfig));
} catch (Exception e) {
// If process died, no one cares.
}
@@ -1365,8 +1374,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
ar.add(rintent);
- app.thread.scheduleNewIntent(
- ar, appToken, state == PAUSED /* andPause */);
+ service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+ new NewIntentItem(ar, state == PAUSED));
unsent = false;
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
@@ -1588,7 +1597,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
setVisible(true);
sleeping = false;
app.pendingUiClean = true;
- app.thread.scheduleWindowVisibility(appToken, true /* showWindow */);
+ service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
+ new WindowVisibilityItem(true /* showWindow */));
// The activity may be waiting for stop, but that is no longer appropriate for it.
mStackSupervisor.mStoppingActivities.remove(this);
mStackSupervisor.mGoingToSleepActivities.remove(this);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 481699833e41..3cf22833c101 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -104,6 +104,13 @@ import android.app.IActivityController;
import android.app.ResultInfo;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.WindowVisibilityItem;
+import android.app.servertransaction.DestroyActivityItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.StopActivityItem;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -1419,8 +1426,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
prev.userId, System.identityHashCode(prev),
prev.shortComponentName);
mService.updateUsageStats(prev, false);
- prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
- userLeaving, prev.configChangeFlags, pauseImmediately);
+
+ mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken,
+ new PauseActivityItem(prev.finishing, userLeaving,
+ prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
@@ -2055,7 +2064,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (r.app != null && r.app.thread != null) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Scheduling invisibility: " + r);
- r.app.thread.scheduleWindowVisibility(r.appToken, false);
+ mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+ new WindowVisibilityItem(false /* showWindow */));
}
// Reset the flag indicating that an app can enter picture-in-picture once the
@@ -2575,13 +2585,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (!next.finishing && N > 0) {
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Delivering results to " + next + ": " + a);
- next.app.thread.scheduleSendResult(next.appToken, a);
+ mService.mLifecycleManager.scheduleTransaction(next.app.thread,
+ next.appToken, new ActivityResultItem(a));
}
}
if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(
- next.newIntents, next.appToken, false /* andPause */);
+ mService.mLifecycleManager.scheduleTransaction(next.app.thread,
+ next.appToken, new NewIntentItem(next.newIntents,
+ false /* andPause */));
}
// Well the app will no longer be stopped.
@@ -2598,8 +2610,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
next.app.pendingUiClean = true;
next.app.forceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
- next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
- mService.isNextTransitionForward(), resumeAnimOptions);
+ mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken,
+ new ResumeActivityItem(next.app.repProcState,
+ mService.isNextTransitionForward()));
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ next);
@@ -3249,7 +3262,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
list.add(new ResultInfo(resultWho, requestCode,
resultCode, data));
- r.app.thread.scheduleSendResult(r.appToken, list);
+ mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+ new ActivityResultItem(list));
return;
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending result to " + r, e);
@@ -3377,7 +3391,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
EventLogTags.writeAmStopActivity(
r.userId, System.identityHashCode(r), r.shortComponentName);
- r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
+ mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+ new StopActivityItem(r.visible, r.configChangeFlags));
if (shouldSleepOrShutDownActivities()) {
r.setSleeping(true);
}
@@ -4009,7 +4024,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// 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 */,
- !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY);
+ !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
}
// We must keep the task around until all activities are destroyed. The following
@@ -4174,8 +4189,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
- r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
- r.configChangeFlags);
+ mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
+ new DestroyActivityItem(r.finishing, r.configChangeFlags));
} catch (Exception e) {
// We can just ignore exceptions here... if the process
// has crashed, our death notification will clean things
@@ -4988,13 +5003,22 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
boolean toTop) {
+ return createTaskRecord(taskId, info, intent, voiceSession, voiceInteractor, toTop,
+ null /*activity*/, null /*source*/, null /*options*/);
+ }
+
+ TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ boolean toTop, ActivityRecord activity, ActivityRecord source,
+ ActivityOptions options) {
final TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
voiceInteractor);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, "createTaskRecord");
final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
.isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
- if (!mStackSupervisor.getLaunchingBoundsController().layoutTask(task, info.windowLayout)
+ if (!mStackSupervisor.getLaunchingBoundsController()
+ .layoutTask(task, info.windowLayout, activity, source, options)
&& !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
task.updateOverrideConfiguration(getOverrideBounds());
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5525cdb380b7..52ee67e628e3 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -114,6 +114,7 @@ import android.app.ResultInfo;
import android.app.WaitResult;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.LaunchActivityItem;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -1392,7 +1393,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.icicle);
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
+ mService.mLifecycleManager.scheduleTransaction(app.thread, r.appToken,
+ new LaunchActivityItem(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
@@ -1400,7 +1402,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, !andResume,
- mService.isNextTransitionForward(), profilerInfo);
+ mService.isNextTransitionForward(), profilerInfo));
if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
// This may be a heavy-weight process! Note that the package
@@ -2737,7 +2739,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
} else {
for (int i = tasks.size() - 1; i >= 0; i--) {
removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
- REMOVE_FROM_RECENTS);
+ REMOVE_FROM_RECENTS, "remove-stack");
}
}
}
@@ -2770,8 +2772,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
/**
* See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
*/
- boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
- return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY);
+ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
+ String reason) {
+ return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY,
+ reason);
}
/**
@@ -2785,10 +2789,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* @return Returns true if the given task was found and removed.
*/
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
- boolean pauseImmediately) {
+ boolean pauseImmediately, String reason) {
final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
- tr.removeTaskActivitiesLocked(pauseImmediately);
+ tr.removeTaskActivitiesLocked(pauseImmediately, reason);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
mService.mLockTaskController.clearLockedTask(tr);
if (tr.isPersistable) {
@@ -2922,7 +2926,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
@Override
public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
- // TODO: Trim active task once b/68045330 is fixed
+ 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, false /* killProcess */,
+ false /* removeFromRecents */, !PAUSE_IMMEDIATELY, "recent-task-trimmed");
+ }
task.removedFromRecents();
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 03162bbe0199..6010422bf8b8 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1756,7 +1756,8 @@ class ActivityStarter {
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
- mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
+ mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
+ mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
updateBounds(mStartActivity.getTask(), mLaunchBounds);
@@ -1965,7 +1966,7 @@ class ActivityStarter {
final ActivityRecord prev = mTargetStack.getTopActivity();
final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
- mIntent, null, null, true);
+ mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
mTargetStack.positionChildWindowContainerAtTop(task);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index 17626ea166af..ab86dbdbd106 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -61,7 +61,7 @@ class AppTaskImpl extends IAppTask.Stub {
try {
// We remove the task from recents to preserve backwards
if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
- REMOVE_FROM_RECENTS)) {
+ REMOVE_FROM_RECENTS, "finish-and-remove-task")) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
} finally {
diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java
new file mode 100644
index 000000000000..c04d103c7ff5
--- /dev/null
+++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java
@@ -0,0 +1,120 @@
+/*
+ * 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.am;
+
+import android.annotation.NonNull;
+import android.app.IApplicationThread;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
+ * and execute them as a single transaction.
+ *
+ * @see ClientTransaction
+ */
+class ClientLifecycleManager {
+ // TODO(lifecycler): Implement building transactions or global transaction.
+ // TODO(lifecycler): Use object pools for transactions and transaction items.
+
+ /**
+ * Schedule a transaction, which may consist of multiple callbacks and a lifecycle request.
+ * @param transaction A sequence of client transaction items.
+ * @throws RemoteException
+ *
+ * @see ClientTransaction
+ */
+ void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
+ transaction.schedule();
+ }
+
+ /**
+ * Schedule a single lifecycle request or callback to client activity.
+ * @param client Target client.
+ * @param activityToken Target activity token.
+ * @param stateRequest A request to move target activity to a desired lifecycle state.
+ * @throws RemoteException
+ *
+ * @see ClientTransactionItem
+ */
+ void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
+ @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
+ final ClientTransaction clientTransaction = transactionWithState(client, activityToken,
+ stateRequest);
+ scheduleTransaction(clientTransaction);
+ }
+
+ /**
+ * Schedule a single callback delivery to client activity.
+ * @param client Target client.
+ * @param activityToken Target activity token.
+ * @param callback A request to deliver a callback.
+ * @throws RemoteException
+ *
+ * @see ClientTransactionItem
+ */
+ void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
+ @NonNull ClientTransactionItem callback) throws RemoteException {
+ final ClientTransaction clientTransaction = transactionWithCallback(client, activityToken,
+ callback);
+ scheduleTransaction(clientTransaction);
+ }
+
+ /**
+ * Schedule a single callback delivery to client application.
+ * @param client Target client.
+ * @param callback A request to deliver a callback.
+ * @throws RemoteException
+ *
+ * @see ClientTransactionItem
+ */
+ void scheduleTransaction(@NonNull IApplicationThread client,
+ @NonNull ClientTransactionItem callback) throws RemoteException {
+ final ClientTransaction clientTransaction = transactionWithCallback(client,
+ null /* activityToken */, callback);
+ scheduleTransaction(clientTransaction);
+ }
+
+ /**
+ * @return A new instance of {@link ClientTransaction} with a single lifecycle state request.
+ *
+ * @see ClientTransaction
+ * @see ClientTransactionItem
+ */
+ private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,
+ @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {
+ final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken);
+ clientTransaction.setLifecycleStateRequest(stateRequest);
+ return clientTransaction;
+ }
+
+ /**
+ * @return A new instance of {@link ClientTransaction} with a single callback invocation.
+ *
+ * @see ClientTransaction
+ * @see ClientTransactionItem
+ */
+ private static ClientTransaction transactionWithCallback(@NonNull IApplicationThread client,
+ IBinder activityToken, @NonNull ClientTransactionItem callback) {
+ final ClientTransaction clientTransaction = new ClientTransaction(client, activityToken);
+ clientTransaction.addCallback(callback);
+ return clientTransaction;
+ }
+}
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 82019fde30b7..65c4a42108ec 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -58,10 +58,6 @@ public final class CompatModePackages {
public static final int COMPAT_FLAG_DONT_ASK = 1<<0;
// Compatibility state: compatibility mode is enabled.
public static final int COMPAT_FLAG_ENABLED = 1<<1;
- // Unsupported zoom state: don't warn the user about unsupported zoom mode.
- public static final int UNSUPPORTED_ZOOM_FLAG_DONT_NOTIFY = 1<<2;
- // Unsupported compile SDK state: don't warn the user about unsupported compile SDK.
- public static final int UNSUPPORTED_COMPILE_SDK_FLAG_DONT_NOTIFY = 1<<3;
private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
@@ -235,14 +231,6 @@ public final class CompatModePackages {
return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
}
- public boolean getPackageNotifyUnsupportedZoomLocked(String packageName) {
- return (getPackageFlags(packageName)&UNSUPPORTED_ZOOM_FLAG_DONT_NOTIFY) == 0;
- }
-
- public boolean getPackageNotifyUnsupportedCompileSdkLocked(String packageName) {
- return (getPackageFlags(packageName)&UNSUPPORTED_COMPILE_SDK_FLAG_DONT_NOTIFY) == 0;
- }
-
public void setFrontActivityAskCompatModeLocked(boolean ask) {
ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
if (r != null) {
@@ -254,14 +242,6 @@ public final class CompatModePackages {
setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
}
- public void setPackageNotifyUnsupportedZoomLocked(String packageName, boolean notify) {
- setPackageFlagLocked(packageName, UNSUPPORTED_ZOOM_FLAG_DONT_NOTIFY, notify);
- }
-
- public void setPackageNotifyUnsupportedCompileSdkLocked(String packageName, boolean notify) {
- setPackageFlagLocked(packageName, UNSUPPORTED_COMPILE_SDK_FLAG_DONT_NOTIFY, notify);
- }
-
private void setPackageFlagLocked(String packageName, int flag, boolean set) {
final int curFlags = getPackageFlags(packageName);
final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag);
diff --git a/services/core/java/com/android/server/am/LaunchingBoundsController.java b/services/core/java/com/android/server/am/LaunchingBoundsController.java
index c762f7f4a507..5aa7f58f0968 100644
--- a/services/core/java/com/android/server/am/LaunchingBoundsController.java
+++ b/services/core/java/com/android/server/am/LaunchingBoundsController.java
@@ -101,8 +101,12 @@ class LaunchingBoundsController {
* @return {@code true} if bounds were set on the task. {@code false} otherwise.
*/
boolean layoutTask(TaskRecord task, WindowLayout layout) {
- calculateBounds(task, layout, null /*activity*/, null /*source*/, null /*options*/,
- mTmpRect);
+ return layoutTask(task, layout, null /*activity*/, null /*source*/, null /*options*/);
+ }
+
+ boolean layoutTask(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+ ActivityRecord source, ActivityOptions options) {
+ calculateBounds(task, layout, activity, source, options, mTmpRect);
if (mTmpRect.isEmpty()) {
return false;
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 6e6d7e9f2945..ebcf8c28079f 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -80,6 +80,20 @@ import java.util.concurrent.TimeUnit;
/**
* Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
* least recent.
+ *
+ * The trimming logic can be boiled down to the following. For recent task list with a number of
+ * tasks, the visible tasks are an interleaving subset of tasks that would normally be presented to
+ * the user. Non-visible tasks are not considered for trimming. Of the visible tasks, only a
+ * sub-range are presented to the user, based on the device type, last task active time, or other
+ * task state. Tasks that are not in the visible range and are not returnable from the SystemUI
+ * (considering the back stack) are considered trimmable. If the device does not support recent
+ * tasks, then trimming is completely disabled.
+ *
+ * eg.
+ * L = [TTTTTTTTTTTTTTTTTTTTTTTTTT] // list of tasks
+ * [VVV VV VVVV V V V ] // Visible tasks
+ * [RRR RR XXXX X X X ] // Visible range tasks, eg. if the device only shows 5 tasks,
+ * // 'X' tasks are trimmed.
*/
class RecentTasks {
private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
@@ -496,7 +510,8 @@ class RecentTasks {
if (tr.userId != userId) return;
if (!taskPackageName.equals(packageName)) return;
- mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+ mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
+ "remove-package-task");
}
}
@@ -513,7 +528,7 @@ class RecentTasks {
&& (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
if (sameComponent) {
mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
- REMOVE_FROM_RECENTS);
+ REMOVE_FROM_RECENTS, "disabled-package");
}
}
}
@@ -1001,12 +1016,13 @@ class RecentTasks {
continue;
} else {
numVisibleTasks++;
- if (isInVisibleRange(task, numVisibleTasks)) {
+ if (isInVisibleRange(task, numVisibleTasks) || !isTrimmable(task)) {
// Keep visible tasks in range
i++;
continue;
} else {
- // Fall through to trim visible tasks that are no longer in range
+ // Fall through to trim visible tasks that are no longer in range and
+ // trimmable
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG,
"Trimming out-of-range visible task=" + task);
}
@@ -1122,6 +1138,28 @@ class RecentTasks {
}
/**
+ * @return whether the given task can be trimmed even if it is outside the visible range.
+ */
+ protected boolean isTrimmable(TaskRecord task) {
+ final ActivityStack stack = task.getStack();
+ final ActivityStack homeStack = mService.mStackSupervisor.mHomeStack;
+
+ // No stack for task, just trim it
+ if (stack == null) {
+ return true;
+ }
+
+ // Ignore tasks from different displays
+ if (stack.getDisplay() != homeStack.getDisplay()) {
+ return false;
+ }
+
+ // Trim tasks that are in stacks that are behind the home stack
+ final ActivityDisplay display = stack.getDisplay();
+ return display.getIndexOf(stack) < display.getIndexOf(homeStack);
+ }
+
+ /**
* If needed, remove oldest existing entries in recents that are for the same kind
* of task as the given one.
*/
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 365990f15835..fd6d618bc62d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1311,7 +1311,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
* Completely remove all activities associated with an existing
* task starting at a specified index.
*/
- final void performClearTaskAtIndexLocked(int activityNdx, boolean pauseImmediately) {
+ final void performClearTaskAtIndexLocked(int activityNdx, boolean pauseImmediately,
+ String reason) {
int numActivities = mActivities.size();
for ( ; activityNdx < numActivities; ++activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
@@ -1325,7 +1326,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
--activityNdx;
--numActivities;
} else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
- "clear-task-index", false, pauseImmediately)) {
+ reason, false, pauseImmediately)) {
--activityNdx;
--numActivities;
}
@@ -1337,7 +1338,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
*/
void performClearTaskLocked() {
mReuseTask = true;
- performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY);
+ performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY, "clear-task-all");
mReuseTask = false;
}
@@ -1408,9 +1409,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return null;
}
- void removeTaskActivitiesLocked(boolean pauseImmediately) {
+ void removeTaskActivitiesLocked(boolean pauseImmediately, String reason) {
// Just remove the entire task.
- performClearTaskAtIndexLocked(0, pauseImmediately);
+ performClearTaskAtIndexLocked(0, pauseImmediately, reason);
}
String lockTaskAuthToString() {
diff --git a/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java b/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
index 600589a40bb8..b6f6ae6b508c 100644
--- a/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
+++ b/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
@@ -39,7 +39,7 @@ public class UnsupportedCompileSdkDialog {
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm);
final CharSequence message = context.getString(R.string.unsupported_compile_sdk_message,
- label, appInfo.compileSdkVersionCodename);
+ label);
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setPositiveButton(R.string.ok, null)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8a9b45b2c555..2d5f7c7124c4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8045,24 +8045,24 @@ public class PackageManagerService extends IPackageManager.Stub
return finalList;
}
- private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
+ private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
- scanDirLI(dir, parseFlags, scanFlags, currentTime);
+ scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
- final File[] files = dir.listFiles();
+ private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
+ final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
- Log.d(TAG, "No files in app dir " + dir);
+ Log.d(TAG, "No files in app dir " + scanDir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
@@ -8094,7 +8094,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
try {
if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
- scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,
+ scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
}
} catch (PackageManagerException e) {
@@ -8126,14 +8126,14 @@ public class PackageManagerService extends IPackageManager.Stub
logCriticalInfo(priority, msg);
}
- private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
+ private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg,
final @ParseFlags int parseFlags) throws PackageManagerException {
// When upgrading from pre-N MR1, verify the package time stamp using the package
// directory and not the APK file.
final long lastModifiedTime = mIsPreNMR1Upgrade
- ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg, srcFile);
+ ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
if (ps != null
- && ps.codePath.equals(srcFile)
+ && ps.codePathString.equals(pkg.codePath)
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
@@ -8156,7 +8156,7 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
- Slog.i(TAG, srcFile.toString() + " changed; collecting certs");
+ Slog.i(TAG, toString() + " changed; collecting certs");
}
try {
@@ -8211,14 +8211,14 @@ public class PackageManagerService extends IPackageManager.Stub
renameStaticSharedLibraryPackage(pkg);
}
- return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+ return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
- private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
+ private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
@@ -8236,20 +8236,20 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Scan the parent
- PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, parseFlags,
+ PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, parseFlags,
scanFlags, currentTime, user);
// Scan the children
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
- scanPackageInternalLI(childPackage, scanFile, parseFlags, scanFlags,
+ scanPackageInternalLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
- return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+ return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
return scannedPkg;
@@ -8259,12 +8259,12 @@ public class PackageManagerService extends IPackageManager.Stub
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
- private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
+ private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
PackageSetting ps = null;
- PackageSetting updatedPkg;
+ PackageSetting updatedPs;
// reader
synchronized (mPackages) {
// Look to see if we already know about this package.
@@ -8281,8 +8281,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
// package name depending on our state.
- updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
- if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
+ updatedPs = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
+ if (DEBUG_INSTALL && updatedPs != null) Slog.d(TAG, "updatedPkg = " + updatedPs);
// If this is a package we don't know about on the system partition, we
// may need to remove disabled child packages on the system partition
@@ -8316,27 +8316,27 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- final boolean isUpdatedPkg = updatedPkg != null;
+ final boolean isUpdatedPkg = updatedPs != null;
final boolean isUpdatedSystemPkg = isUpdatedPkg && (scanFlags & SCAN_AS_SYSTEM) != 0;
boolean isUpdatedPkgBetter = false;
// First check if this is a system package that may involve an update
if (isUpdatedSystemPkg) {
// If new package is not located in "/system/priv-app" (e.g. due to an OTA),
// it needs to drop FLAG_PRIVILEGED.
- if (locationIsPrivileged(scanFile)) {
- updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+ if (locationIsPrivileged(pkg.codePath)) {
+ updatedPs.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
} else {
- updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+ updatedPs.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
// If new package is not located in "/oem" (e.g. due to an OTA),
// it needs to drop FLAG_OEM.
- if (locationIsOem(scanFile)) {
- updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
+ if (locationIsOem(pkg.codePath)) {
+ updatedPs.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
} else {
- updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_OEM;
+ updatedPs.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_OEM;
}
- if (ps != null && !ps.codePath.equals(scanFile)) {
+ if (ps != null && !ps.codePathString.equals(pkg.codePath)) {
// The path has changed from what was last scanned... check the
// version of the new path against what we have stored to determine
// what to do.
@@ -8344,26 +8344,27 @@ public class PackageManagerService extends IPackageManager.Stub
if (pkg.mVersionCode <= ps.versionCode) {
// The system package has been updated and the code path does not match
// Ignore entry. Skip it.
- if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile
+ if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + pkg.codePath
+ " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
- if (!updatedPkg.codePath.equals(scanFile)) {
+ if (!updatedPs.codePathString.equals(pkg.codePath)) {
Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
- + ps.name + " changing from " + updatedPkg.codePathString
- + " to " + scanFile);
- updatedPkg.codePath = scanFile;
- updatedPkg.codePathString = scanFile.toString();
- updatedPkg.resourcePath = scanFile;
- updatedPkg.resourcePathString = scanFile.toString();
+ + ps.name + " changing from " + updatedPs.codePathString
+ + " to " + pkg.codePath);
+ final File codePath = new File(pkg.codePath);
+ updatedPs.codePath = codePath;
+ updatedPs.codePathString = pkg.codePath;
+ updatedPs.resourcePath = codePath;
+ updatedPs.resourcePathString = pkg.codePath;
}
- updatedPkg.pkg = pkg;
- updatedPkg.versionCode = pkg.mVersionCode;
+ updatedPs.pkg = pkg;
+ updatedPs.versionCode = pkg.mVersionCode;
// Update the disabled system child packages to point to the package too.
- final int childCount = updatedPkg.childPackageNames != null
- ? updatedPkg.childPackageNames.size() : 0;
+ final int childCount = updatedPs.childPackageNames != null
+ ? updatedPs.childPackageNames.size() : 0;
for (int i = 0; i < childCount; i++) {
- String childPackageName = updatedPkg.childPackageNames.get(i);
+ String childPackageName = updatedPs.childPackageNames.get(i);
PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
childPackageName);
if (updatedChildPkg != null) {
@@ -8384,7 +8385,7 @@ public class PackageManagerService extends IPackageManager.Stub
mPackages.remove(ps.name);
}
- logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
+ logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
+ " reverting from " + ps.codePathString
+ ": new version " + pkg.mVersionCode
+ " better than installed " + ps.versionCode);
@@ -8431,16 +8432,16 @@ public class PackageManagerService extends IPackageManager.Stub
if (isUpdatedSystemPkg && !isUpdatedPkgBetter) {
// Set CPU Abis to application info.
if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
- final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPkg);
- derivePackageAbi(pkg, scanFile, cpuAbiOverride, false, mAppLib32InstallDir);
+ final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPs);
+ derivePackageAbi(pkg, cpuAbiOverride, false, mAppLib32InstallDir);
} else {
- pkg.applicationInfo.primaryCpuAbi = updatedPkg.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = updatedPkg.secondaryCpuAbiString;
+ pkg.applicationInfo.primaryCpuAbi = updatedPs.primaryCpuAbiString;
+ pkg.applicationInfo.secondaryCpuAbi = updatedPs.secondaryCpuAbiString;
}
- pkg.mExtras = updatedPkg;
+ pkg.mExtras = updatedPs;
throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
- + scanFile + " ignored: updated version " + ps.versionCode
+ + pkg.codePath + " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
}
@@ -8450,19 +8451,19 @@ public class PackageManagerService extends IPackageManager.Stub
// An updated privileged application will not have the PARSE_IS_PRIVILEGED
// flag set initially
- if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ if ((updatedPs.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
// An updated OEM app will not have the PARSE_IS_OEM
// flag set initially
- if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+ if ((updatedPs.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
scanFlags |= SCAN_AS_OEM;
}
}
// Verify certificates against what was last scanned
- collectCertificatesLI(ps, pkg, scanFile, parseFlags);
+ collectCertificatesLI(ps, pkg, parseFlags);
/*
* A new system app appeared, but we already had a non-system one of the
@@ -8492,7 +8493,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
if (pkg.mVersionCode <= ps.versionCode) {
shouldHideSystemApp = true;
- logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile
+ logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + pkg.codePath
+ " but new version " + pkg.mVersionCode + " better than installed "
+ ps.versionCode + "; hiding system");
} else {
@@ -8502,7 +8503,7 @@ public class PackageManagerService extends IPackageManager.Stub
* already-installed application and replace it with our own
* while keeping the application data.
*/
- logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
+ logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
+ " reverting from " + ps.codePathString + ": new version "
+ pkg.mVersionCode + " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
@@ -9976,8 +9977,7 @@ public class PackageManagerService extends IPackageManager.Stub
if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
final boolean extractNativeLibs = !pkg.isLibrary();
- derivePackageAbi(pkg, scanFile, cpuAbiOverride, extractNativeLibs,
- mAppLib32InstallDir);
+ derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Some system apps still use directory structure for native libraries
@@ -10084,7 +10084,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(pkg, scanFile);
+ final long scanFileTime = getLastModifiedTime(pkg);
if (currentTime != 0) {
if (pkgSetting.firstInstallTime == 0) {
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
@@ -10864,10 +10864,9 @@ public class PackageManagerService extends IPackageManager.Stub
*
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
- private static void derivePackageAbi(PackageParser.Package pkg, File scanFile,
- String cpuAbiOverride, boolean extractLibs,
- File appLib32InstallDir)
- throws PackageManagerException {
+ private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
+ boolean extractLibs, File appLib32InstallDir)
+ throws PackageManagerException {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
setNativeLibraryPaths(pkg, appLib32InstallDir);
@@ -15000,7 +14999,12 @@ public class PackageManagerService extends IPackageManager.Stub
resourceFile = afterCodeFile;
// Reflect the rename in scanned details
- pkg.setCodePath(afterCodeFile.getAbsolutePath());
+ try {
+ pkg.setCodePath(afterCodeFile.getCanonicalPath());
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
+ return false;
+ }
pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, pkg.baseCodePath));
pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
@@ -16482,8 +16486,7 @@ public class PackageManagerService extends IPackageManager.Stub
String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
args.abiOverride : pkg.cpuAbiOverride);
final boolean extractNativeLibs = !pkg.isLibrary();
- derivePackageAbi(pkg, new File(pkg.codePath), abiOverride,
- extractNativeLibs, mAppLib32InstallDir);
+ derivePackageAbi(pkg, abiOverride, extractNativeLibs, mAppLib32InstallDir);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
@@ -17487,21 +17490,19 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- static boolean locationIsPrivileged(File path) {
+ static boolean locationIsPrivileged(String path) {
try {
- final String privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app")
- .getCanonicalPath();
- return path.getCanonicalPath().startsWith(privilegedAppDir);
+ final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
+ return path.startsWith(privilegedAppDir.getCanonicalPath());
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
return false;
}
- static boolean locationIsOem(File path) {
+ static boolean locationIsOem(String path) {
try {
- return path.getCanonicalPath().startsWith(
- Environment.getOemDirectory().getCanonicalPath());
+ return path.startsWith(Environment.getOemDirectory().getCanonicalPath());
} catch (IOException e) {
Slog.e(TAG, "Unable to access code path " + path);
}
@@ -17597,7 +17598,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
- installPackageFromSystemLIF(disabledPs.codePath, false /*isPrivileged*/, allUserHandles,
+ installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles,
outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
@@ -17614,7 +17615,7 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Installs a package that's already on the system partition.
*/
- private PackageParser.Package installPackageFromSystemLIF(@NonNull File codePath,
+ private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,
boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
@Nullable PermissionsState origPermissionState, boolean writeSettings)
throws PackageManagerException {
@@ -17623,13 +17624,14 @@ public class PackageManagerService extends IPackageManager.Stub
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- if (isPrivileged || locationIsPrivileged(codePath)) {
+ if (isPrivileged || locationIsPrivileged(codePathString)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
- if (locationIsOem(codePath)) {
+ if (locationIsOem(codePathString)) {
scanFlags |= SCAN_AS_OEM;
}
+ final File codePath = new File(codePathString);
final PackageParser.Package pkg =
scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
@@ -19741,7 +19743,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
// until we can disable the package later.
enableSystemPackageLPw(deletedPkg);
}
- installPackageFromSystemLIF(new File(deletedPkg.codePath),
+ installPackageFromSystemLIF(deletedPkg.codePath,
false /*isPrivileged*/, null /*allUserHandles*/,
null /*origUserHandles*/, null /*origPermissionsState*/,
true /*writeSettings*/);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 758abd76950b..fb6278a43950 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -295,19 +295,20 @@ public class PackageManagerServiceUtils {
return false;
}
- public static long getLastModifiedTime(PackageParser.Package pkg, File srcFile) {
- if (srcFile.isDirectory()) {
- final File baseFile = new File(pkg.baseCodePath);
- long maxModifiedTime = baseFile.lastModified();
- if (pkg.splitCodePaths != null) {
- for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
- final File splitFile = new File(pkg.splitCodePaths[i]);
- maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
- }
+ public static long getLastModifiedTime(PackageParser.Package pkg) {
+ final File srcFile = new File(pkg.codePath);
+ if (!srcFile.isDirectory()) {
+ return srcFile.lastModified();
+ }
+ final File baseFile = new File(pkg.baseCodePath);
+ long maxModifiedTime = baseFile.lastModified();
+ if (pkg.splitCodePaths != null) {
+ for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
+ final File splitFile = new File(pkg.splitCodePaths[i]);
+ maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
}
- return maxModifiedTime;
}
- return srcFile.lastModified();
+ return maxModifiedTime;
}
/**
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7077fd166987..ddad6774871d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3572,11 +3572,10 @@ public final class Settings {
int pkgFlags = 0;
int pkgPrivateFlags = 0;
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- final File codePathFile = new File(codePathStr);
- if (PackageManagerService.locationIsPrivileged(codePathFile)) {
+ if (PackageManagerService.locationIsPrivileged(codePathStr)) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
- PackageSetting ps = new PackageSetting(name, realName, codePathFile,
+ PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
parentPackageName, null /*childPackageNames*/, 0 /*sharedUserId*/, null, null);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ddd3bbdf561b..7415ec3881b1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -240,6 +240,7 @@ import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
@@ -1053,7 +1054,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private ImmersiveModeConfirmation mImmersiveModeConfirmation;
- private SystemGesturesPointerEventListener mSystemGestures;
+ @VisibleForTesting
+ SystemGesturesPointerEventListener mSystemGestures;
IStatusBarService getStatusBarService() {
synchronized (mServiceAquireLock) {
@@ -2664,17 +2666,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// The status bar is the only window allowed to exhibit keyguard behavior.
attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
+ }
+ private int getImpliedSysUiFlagsForLayout(LayoutParams attrs) {
+ int impliedFlags = 0;
if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
- attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
}
final boolean forceWindowDrawsStatusBarBackground =
(attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
|| forceWindowDrawsStatusBarBackground
&& attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
- attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
}
+ return impliedFlags;
}
void readLidState() {
@@ -2724,7 +2730,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void onConfigurationChanged() {
// TODO(multi-display): Define policy for secondary displays.
- Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+ Context uiContext = getSystemUiContext();
final Resources res = uiContext.getResources();
mStatusBarHeight =
@@ -2765,6 +2771,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ @VisibleForTesting
+ Context getSystemUiContext() {
+ return ActivityThread.currentActivityThread().getSystemUiContext();
+ }
+
@Override
public int getMaxWallpaperLayer() {
return getWindowLayerFromTypeLw(TYPE_STATUS_BAR);
@@ -4825,7 +4836,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final int fl = PolicyControl.getWindowFlags(win, attrs);
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
- final int sysUiFl = PolicyControl.getSystemUiVisibility(win, null);
+ final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(win, null);
+ final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
final Rect pf = mTmpParentFrame;
final Rect df = mTmpDisplayFrame;
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 8d56eb80181a..5f067d496e9a 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -213,7 +213,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
*/
public void computeFrameLw(Rect parentFrame, Rect displayFrame,
Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
- Rect stableFrame, Rect outsetFrame);
+ Rect stableFrame, @Nullable Rect outsetFrame);
/**
* Retrieve the current frame of the window that has been assigned by
@@ -808,11 +808,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
return 15;
- case TYPE_STATUS_BAR_SUB_PANEL:
- return 17;
case TYPE_STATUS_BAR:
- return 18;
+ return 17;
case TYPE_STATUS_BAR_PANEL:
+ return 18;
+ case TYPE_STATUS_BAR_SUB_PANEL:
return 19;
case TYPE_KEYGUARD_DIALOG:
return 20;
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 87c92744cdab..7c234f96ee13 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -69,6 +69,7 @@ public class BatterySaverPolicy extends ContentObserver {
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
+ private static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check";
private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
@@ -190,6 +191,12 @@ public class BatterySaverPolicy extends ContentObserver {
private boolean mForceAllAppsStandby;
/**
+ * Whether to put all apps in the stand-by mode.
+ */
+ @GuardedBy("mLock")
+ private boolean mForceBackgroundCheck;
+
+ /**
* Weather to show non-essential sensors (e.g. edge sensors) or not.
*/
@GuardedBy("mLock")
@@ -326,6 +333,7 @@ public class BatterySaverPolicy extends ContentObserver {
mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
mLaunchBoostDisabled = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED, true);
mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
+ mForceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, true);
mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
// Get default value from Settings.Secure
@@ -395,6 +403,12 @@ public class BatterySaverPolicy extends ContentObserver {
case ServiceType.VIBRATION:
return builder.setBatterySaverEnabled(mVibrationDisabled)
.build();
+ case ServiceType.FORCE_ALL_APPS_STANDBY:
+ return builder.setBatterySaverEnabled(mForceAllAppsStandby)
+ .build();
+ case ServiceType.FORCE_BACKGROUND_CHECK:
+ return builder.setBatterySaverEnabled(mForceBackgroundCheck)
+ .build();
case ServiceType.OPTIONAL_SENSORS:
return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
.build();
@@ -438,6 +452,7 @@ public class BatterySaverPolicy extends ContentObserver {
pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
+ pw.println(" " + KEY_FORCE_BACKGROUND_CHECK + "=" + mForceBackgroundCheck);
pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
pw.println();
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 483d5181f9c9..80bc935959a2 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -16,6 +16,7 @@
package com.android.server.power.batterysaver;
import android.Manifest;
+import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -109,6 +110,9 @@ public class BatterySaverController implements BatterySaverPolicyListener {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mReceiver, filter);
+
+ mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class)
+ .isRuntimeRestarted());
}
private PowerManager getPowerManager() {
diff --git a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
index cc1b540e669b..e0ab9e93a8a6 100644
--- a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
+++ b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
@@ -16,19 +16,34 @@
package com.android.server.power.batterysaver;
import android.content.Context;
+import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemProperties;
import android.util.ArrayMap;
+import android.util.AtomicFile;
import android.util.Slog;
+import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
import com.android.server.IoThread;
import libcore.io.IoUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Map;
@@ -47,6 +62,14 @@ public class FileUpdater {
private static final boolean DEBUG = BatterySaverController.DEBUG;
+ /**
+ * If this system property is set to 1, it'll skip all file writes. This can be used when
+ * one needs to change max CPU frequency for benchmarking, for example.
+ */
+ private static final String PROP_SKIP_WRITE = "debug.batterysaver.no_write_files";
+
+ private static final String TAG_DEFAULT_ROOT = "defaults";
+
// Don't do disk access with this lock held.
private final Object mLock = new Object();
@@ -93,6 +116,21 @@ public class FileUpdater {
RETRY_INTERVAL_MS = retryIntervalMs;
}
+ public void systemReady(boolean runtimeRestarted) {
+ synchronized (mLock) {
+ if (runtimeRestarted) {
+ // If it runtime restarted, read the original values from the disk and apply.
+ if (loadDefaultValuesLocked()) {
+ Slog.d(TAG, "Default values loaded after runtime restart; writing them...");
+ restoreDefault();
+ }
+ } else {
+ // Delete it, without checking the result. (file-not-exist is not an exception.)
+ injectDefaultValuesFilename().delete();
+ }
+ }
+ }
+
/**
* Write values to files. (Note the actual writes happen ASAP but asynchronously.)
*/
@@ -241,6 +279,7 @@ public class FileUpdater {
}
synchronized (mLock) {
mDefaultValues.put(file, originalValue);
+ saveDefaultValuesLocked();
}
return true;
}
@@ -252,17 +291,92 @@ public class FileUpdater {
@VisibleForTesting
void injectWriteToFile(String file, String value) throws IOException {
+ if (injectShouldSkipWrite()) {
+ Slog.i(TAG, "Skipped writing to '" + file + "'");
+ return;
+ }
if (DEBUG) {
Slog.d(TAG, "Writing: '" + value + "' to '" + file + "'");
}
try (FileWriter out = new FileWriter(file)) {
out.write(value);
- } catch (IOException e) {
+ } catch (IOException | RuntimeException e) {
Slog.w(TAG, "Failed writing '" + value + "' to '" + file + "': " + e.getMessage());
throw e;
}
}
+ private void saveDefaultValuesLocked() {
+ final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
+
+ FileOutputStream outs = null;
+ try {
+ file.getBaseFile().getParentFile().mkdirs();
+ outs = file.startWrite();
+
+ // Write to XML
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(outs, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, TAG_DEFAULT_ROOT);
+
+ XmlUtils.writeMapXml(mDefaultValues, out, null);
+
+ // Epilogue.
+ out.endTag(null, TAG_DEFAULT_ROOT);
+ out.endDocument();
+
+ // Close.
+ file.finishWrite(outs);
+ } catch (IOException | XmlPullParserException | RuntimeException e) {
+ Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
+ file.failWrite(outs);
+ }
+ }
+
+ @VisibleForTesting
+ boolean loadDefaultValuesLocked() {
+ final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
+ if (DEBUG) {
+ Slog.d(TAG, "Loading from " + file.getBaseFile());
+ }
+ Map<String, String> read = null;
+ try (FileInputStream in = file.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ // Check the root tag
+ final String tag = parser.getName();
+ if (depth == 1) {
+ if (!TAG_DEFAULT_ROOT.equals(tag)) {
+ Slog.e(TAG, "Invalid root tag: " + tag);
+ return false;
+ }
+ continue;
+ }
+ final String[] tagName = new String[1];
+ read = (ArrayMap<String, String>) XmlUtils.readThisArrayMapXml(parser,
+ TAG_DEFAULT_ROOT, tagName, null);
+ }
+ } catch (FileNotFoundException e) {
+ read = null;
+ } catch (IOException | XmlPullParserException | RuntimeException e) {
+ Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
+ }
+ if (read != null) {
+ mDefaultValues.clear();
+ mDefaultValues.putAll(read);
+ return true;
+ }
+ return false;
+ }
+
private void doWtf(String message) {
injectWtf(message, null);
}
@@ -271,4 +385,20 @@ public class FileUpdater {
void injectWtf(String message, Throwable e) {
Slog.wtf(TAG, message, e);
}
+
+ File injectDefaultValuesFilename() {
+ final File dir = new File(Environment.getDataSystemDirectory(), "battery-saver");
+ dir.mkdirs();
+ return new File(dir, "default-values.xml");
+ }
+
+ @VisibleForTesting
+ boolean injectShouldSkipWrite() {
+ return SystemProperties.getBoolean(PROP_SKIP_WRITE, false);
+ }
+
+ @VisibleForTesting
+ ArrayMap<String, String> getDefaultValuesForTest() {
+ return mDefaultValues;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 409e04b94b04..41348ba4b041 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -181,8 +181,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final TaskStackContainers mTaskStackContainers = new TaskStackContainers();
// Contains all non-app window containers that should be displayed above the app containers
// (e.g. Status bar)
- private final NonAppWindowContainers mAboveAppWindowsContainers =
- new NonAppWindowContainers("mAboveAppWindowsContainers");
+ private final AboveAppWindowContainers mAboveAppWindowsContainers =
+ new AboveAppWindowContainers("mAboveAppWindowsContainers");
// Contains all non-app window containers that should be displayed below the app containers
// (e.g. Wallpaper).
private final NonAppWindowContainers mBelowAppWindowsContainers =
@@ -356,7 +356,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/**
* We organize all top-level Surfaces in to the following layers.
* mOverlayLayer contains a few Surfaces which are always on top of others
- * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay})
+ * and omitted from Screen-Magnification, for example the strict mode flash or
+ * the magnification overlay itself.
* {@link #mWindowingLayer} contains everything else.
*/
private SurfaceControl mOverlayLayer;
@@ -2968,227 +2969,30 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Takes a snapshot of the display. In landscape mode this grabs the whole screen.
* In portrait mode, it grabs the full screenshot.
*
- * @param width the width of the target bitmap
- * @param height the height of the target bitmap
- * @param includeFullDisplay true if the screen should not be cropped before capture
- * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
* @param config of the output bitmap
* @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
- * @param includeDecor whether to include window decors, like the status or navigation bar
- * background of the window
*/
- Bitmap screenshotApplications(IBinder appToken, int width, int height,
- boolean includeFullDisplay, float frameScale, Bitmap.Config config,
- boolean wallpaperOnly, boolean includeDecor) {
- Bitmap bitmap = screenshotApplications(appToken, width, height, includeFullDisplay,
- frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshot);
- if (bitmap == null) {
- return null;
- }
-
- if (DEBUG_SCREENSHOT) {
- // TEST IF IT's ALL BLACK
- int[] buffer = new int[bitmap.getWidth() * bitmap.getHeight()];
- bitmap.getPixels(buffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(),
- bitmap.getHeight());
- boolean allBlack = true;
- final int firstColor = buffer[0];
- for (int i = 0; i < buffer.length; i++) {
- if (buffer[i] != firstColor) {
- allBlack = false;
- break;
- }
- }
- if (allBlack) {
- final WindowState appWin = mScreenshotApplicationState.appWin;
- final int maxLayer = mScreenshotApplicationState.maxLayer;
- final int minLayer = mScreenshotApplicationState.minLayer;
- Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
- Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
- (appWin != null ?
- appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
- " minLayer=" + minLayer + " maxLayer=" + maxLayer);
- }
- }
-
- // Create a copy of the screenshot that is immutable and backed in ashmem.
- // This greatly reduces the overhead of passing the bitmap between processes.
- Bitmap ret = bitmap.createAshmemBitmap(config);
- bitmap.recycle();
- return ret;
- }
-
- GraphicBuffer screenshotApplicationsToBuffer(IBinder appToken, int width, int height,
- boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
- boolean includeDecor) {
- return screenshotApplications(appToken, width, height, includeFullDisplay, frameScale,
- wallpaperOnly, includeDecor, SurfaceControl::screenshotToBuffer);
- }
-
- private <E> E screenshotApplications(IBinder appToken, int width, int height,
- boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
- boolean includeDecor, Screenshoter<E> screenshoter) {
- int dw = mDisplayInfo.logicalWidth;
- int dh = mDisplayInfo.logicalHeight;
- if (dw == 0 || dh == 0) {
- if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
- + ": returning null. logical widthxheight=" + dw + "x" + dh);
- return null;
- }
-
- E bitmap;
-
- mScreenshotApplicationState.reset(appToken == null && !wallpaperOnly);
- final Rect frame = new Rect();
- final Rect stackBounds = new Rect();
-
- final int aboveAppLayer = (mService.mPolicy.getWindowLayerFromTypeLw(TYPE_APPLICATION) + 1)
- * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
- final MutableBoolean mutableIncludeFullDisplay = new MutableBoolean(includeFullDisplay);
- synchronized(mService.mWindowMap) {
+ Bitmap screenshotDisplay(Bitmap.Config config, boolean wallpaperOnly) {
+ synchronized (mService.mWindowMap) {
if (!mService.mPolicy.isScreenOn()) {
- if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Attempted to take screenshot while display"
- + " was off.");
- return null;
- }
- // Figure out the part of the screen that is actually the app.
- mScreenshotApplicationState.appWin = null;
- forAllWindows(w -> {
- if (!w.mHasSurface) {
- return false;
- }
- if (w.mLayer >= aboveAppLayer) {
- return false;
+ if (DEBUG_SCREENSHOT) {
+ Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
}
- if (wallpaperOnly && !w.mIsWallpaper) {
- return false;
- }
- if (w.mIsImWindow) {
- return false;
- } else if (w.mIsWallpaper) {
- // If this is the wallpaper layer and we're only looking for the wallpaper layer
- // then the target window state is this one.
- if (wallpaperOnly) {
- mScreenshotApplicationState.appWin = w;
- }
-
- if (mScreenshotApplicationState.appWin == null) {
- // We have not ran across the target window yet, so it is probably behind
- // the wallpaper. This can happen when the keyguard is up and all windows
- // are moved behind the wallpaper. We don't want to include the wallpaper
- // layer in the screenshot as it will cover-up the layer of the target
- // window.
- return false;
- }
- // Fall through. The target window is in front of the wallpaper. For this
- // case we want to include the wallpaper layer in the screenshot because
- // the target window might have some transparent areas.
- } else if (appToken != null) {
- if (w.mAppToken == null || w.mAppToken.token != appToken) {
- // This app window is of no interest if it is not associated with the
- // screenshot app.
- return false;
- }
- mScreenshotApplicationState.appWin = w;
- }
-
- // Include this window.
-
- final WindowStateAnimator winAnim = w.mWinAnimator;
-
- // Don't include wallpaper in bounds calculation
- if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
- if (includeDecor) {
- final Task task = w.getTask();
- if (task != null) {
- task.getBounds(frame);
- } else {
-
- // No task bounds? Too bad! Ain't no screenshot then.
- return true;
- }
- } else {
- final Rect wf = w.mFrame;
- final Rect cr = w.mContentInsets;
- int left = wf.left + cr.left;
- int top = wf.top + cr.top;
- int right = wf.right - cr.right;
- int bottom = wf.bottom - cr.bottom;
- frame.union(left, top, right, bottom);
- w.getVisibleBounds(stackBounds);
- if (!Rect.intersects(frame, stackBounds)) {
- // Set frame empty if there's no intersection.
- frame.setEmpty();
- }
- }
- }
-
- final boolean foundTargetWs =
- (w.mAppToken != null && w.mAppToken.token == appToken)
- || (mScreenshotApplicationState.appWin != null && wallpaperOnly);
- if (foundTargetWs && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
- mScreenshotApplicationState.screenshotReady = true;
- }
-
- if (w.isObscuringDisplay()){
- return true;
- }
- return false;
- }, true /* traverseTopToBottom */);
-
- final WindowState appWin = mScreenshotApplicationState.appWin;
- final boolean screenshotReady = mScreenshotApplicationState.screenshotReady;
-
- if (appToken != null && appWin == null) {
- // Can't find a window to snapshot.
- if (DEBUG_SCREENSHOT) Slog.i(TAG_WM,
- "Screenshot: Couldn't find a surface matching " + appToken);
return null;
}
- if (!screenshotReady) {
- Slog.i(TAG_WM, "Failed to capture screenshot of " + appToken +
- " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
- appWin.mWinAnimator.mDrawState)));
+ if (wallpaperOnly && !shouldScreenshotWallpaper()) {
return null;
}
- // Screenshot is ready to be taken. Everything from here below will continue
- // through the bottom of the loop and return a value. We only stay in the loop
- // because we don't want to release the mWindowMap lock until the screenshot is
- // taken.
-
+ int dw = mDisplayInfo.logicalWidth;
+ int dh = mDisplayInfo.logicalHeight;
- if (!mutableIncludeFullDisplay.value) {
- // Constrain frame to the screen size.
- if (!frame.intersect(0, 0, dw, dh)) {
- frame.setEmpty();
- }
- } else {
- // Caller just wants entire display.
- frame.set(0, 0, dw, dh);
- }
- if (frame.isEmpty()) {
+ if (dw <= 0 || dh <= 0) {
return null;
}
- if (width < 0) {
- width = (int) (frame.width() * frameScale);
- }
- if (height < 0) {
- height = (int) (frame.height() * frameScale);
- }
-
- // Tell surface flinger what part of the image to crop. Take the top
- // right part of the application, and crop the larger dimension to fit.
- Rect crop = new Rect(frame);
- if (width / (float) frame.width() < height / (float) frame.height()) {
- int cropWidth = (int)((float)width / (float)height * frame.height());
- crop.right = crop.left + cropWidth;
- } else {
- int cropHeight = (int)((float)height / (float)width * frame.width());
- crop.bottom = crop.top + cropHeight;
- }
+ final Rect frame = new Rect(0, 0, dw, dh);
// The screenshot API does not apply the current screen rotation.
int rot = mDisplay.getRotation();
@@ -3197,43 +3001,52 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
rot = (rot == ROTATION_90) ? ROTATION_270 : ROTATION_90;
}
- // Surfaceflinger is not aware of orientation, so convert our logical
- // crop to surfaceflinger's portrait orientation.
- convertCropForSurfaceFlinger(crop, rot, dw, dh);
-
- if (DEBUG_SCREENSHOT) {
- forAllWindows(w -> {
- final WindowSurfaceController controller = w.mWinAnimator.mSurfaceController;
- Slog.i(TAG_WM, w + ": " + w.mLayer
- + " animLayer=" + w.mWinAnimator.mAnimLayer
- + " surfaceLayer=" + ((controller == null)
- ? "null" : controller.getLayer()));
- }, false /* traverseTopToBottom */);
- }
+ // SurfaceFlinger is not aware of orientation, so convert our logical
+ // crop to SurfaceFlinger's portrait orientation.
+ convertCropForSurfaceFlinger(frame, rot, dw, dh);
final ScreenRotationAnimation screenRotationAnimation =
mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
final boolean inRotation = screenRotationAnimation != null &&
screenRotationAnimation.isAnimating();
- if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
- "Taking screenshot while rotating");
-
- // We force pending transactions to flush before taking
- // the screenshot by pushing an empty synchronous transaction.
- SurfaceControl.openTransaction();
- SurfaceControl.closeTransactionSync();
+ if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
// TODO(b/68392460): We should screenshot Task controls directly
// but it's difficult at the moment as the Task doesn't have the
// correct size set.
- bitmap = screenshoter.screenshot(crop, width, height, 0, 1,
- inRotation, rot);
+ final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, 0, 1, inRotation, rot);
if (bitmap == null) {
Slog.w(TAG_WM, "Failed to take screenshot");
return null;
}
+
+ // Create a copy of the screenshot that is immutable and backed in ashmem.
+ // This greatly reduces the overhead of passing the bitmap between processes.
+ final Bitmap ret = bitmap.createAshmemBitmap(config);
+ bitmap.recycle();
+ return ret;
}
- return bitmap;
+ }
+
+ private boolean shouldScreenshotWallpaper() {
+ MutableBoolean screenshotReady = new MutableBoolean(false);
+
+ forAllWindows(w -> {
+ if (!w.mIsWallpaper) {
+ return false;
+ }
+
+ // Found the wallpaper window
+ final WindowStateAnimator winAnim = w.mWinAnimator;
+
+ if (winAnim.getShown() && winAnim.mLastAlpha > 0f) {
+ screenshotReady.value = true;
+ }
+
+ return true;
+ }, true /* traverseTopToBottom */);
+
+ return screenshotReady.value;
}
// TODO: Can this use createRotationMatrix()?
@@ -3746,11 +3559,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
+ private final class AboveAppWindowContainers extends NonAppWindowContainers {
+ AboveAppWindowContainers(String name) {
+ super(name);
+ }
+
+ void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) {
+ boolean needAssignIme = imeContainer != null
+ && imeContainer.getSurfaceControl() != null;
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowToken wt = mChildren.get(j);
+ wt.assignLayer(t, j);
+ wt.assignChildLayers(t);
+
+ int layer = mService.mPolicy.getWindowLayerFromTypeLw(
+ wt.windowType, wt.mOwnerCanManageAppTokens);
+ if (needAssignIme && layer >= TYPE_INPUT_METHOD_DIALOG) {
+ t.setRelativeLayer(imeContainer.getSurfaceControl(),
+ wt.getSurfaceControl(), -1);
+ needAssignIme = false;
+ }
+ }
+ if (needAssignIme) {
+ t.setRelativeLayer(imeContainer.getSurfaceControl(),
+ getSurfaceControl(), Integer.MIN_VALUE);
+ }
+ }
+ }
+
/**
* Window container class that contains all containers on this display that are not related to
* Apps. E.g. status bar.
*/
- private final class NonAppWindowContainers extends DisplayChildWindowContainer<WindowToken> {
+ private class NonAppWindowContainers extends DisplayChildWindowContainer<WindowToken> {
/**
* Compares two child window tokens returns -1 if the first is lesser than the second in
* terms of z-order and 1 otherwise.
@@ -3819,15 +3660,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- /**
- * Interface to screenshot into various types, i.e. {@link Bitmap} and {@link GraphicBuffer}.
- */
- @FunctionalInterface
- private interface Screenshoter<E> {
- E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean useIdentityTransform, int rotation);
- }
-
SurfaceControl.Builder makeSurface(SurfaceSession s) {
return mService.makeSurfaceBuilder(s)
.setParent(mWindowingLayer);
@@ -3848,12 +3680,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return b;
}
- b.setName(child.getName());
- if (child.isScreenOverlay()) {
- return b.setParent(mOverlayLayer);
- } else {
- return b.setParent(mWindowingLayer);
- }
+ return b.setName(child.getName())
+ .setParent(mWindowingLayer);
}
/**
@@ -3891,39 +3719,36 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mAboveAppWindowsContainers.assignLayer(t, 2);
WindowState imeTarget = mService.mInputMethodTarget;
+ boolean needAssignIme = true;
- // A brief summary of IME layer assignment:
+ // In the case where we have an IME target that is not in split-screen
+ // mode IME assignment is easy. We just need the IME to go directly above
+ // the target. This way children of the target will naturally go above the IME
+ // and everyone is happy.
//
- // In case we have no IME target but we have an IME we are typically in
- // a transition state and keeping the IME on top of everything except overlays
- // seems to work best.
+ // In the case of split-screen windowing mode, we need to elevate the IME above the
+ // docked divider while keeping the app itself below the docked divider, so instead
+ // we use relative layering of the IME targets child windows, and place the
+ // IME in the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
//
- // In split-screen windowing mode we can't layer the
- // IME relative to the IME target because it needs to
- // go over the docked divider, so instead we place it on top
- // of everything and use relative layering of windows which need
- // to go above it (see special logic in WindowState#assignLayer)
- //
- // There is a third case, where the IME target has no SurfaceControl, for
- // example if Layer assignment were triggered due to removal of the
- // IME target while it was still the IME target.
- if (imeTarget == null ||
- imeTarget.inSplitScreenWindowingMode() ||
- imeTarget.getSurfaceControl() == null) {
- mImeWindowsContainers.assignLayer(t, 3);
- } else {
+ // In the case where we have no IME target we assign it where it's base layer would
+ // place it in the AboveAppWindowContainers.
+ if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode()
+ && (imeTarget.getSurfaceControl() != null)) {
t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
imeTarget.getSurfaceControl(),
// TODO: We need to use an extra level on the app surface to ensure
// this is always above SurfaceView but always below attached window.
1);
+ needAssignIme = false;
}
// Above we have assigned layers to our children, now we ask them to assign
// layers to their children.
mBelowAppWindowsContainers.assignChildLayers(t);
mTaskStackContainers.assignChildLayers(t);
- mAboveAppWindowsContainers.assignChildLayers(t);
+ mAboveAppWindowsContainers.assignChildLayers(t,
+ needAssignIme == true ? mImeWindowsContainers : null);
mImeWindowsContainers.assignChildLayers(t);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e81d366bd85a..112e62f27ea2 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -645,15 +645,15 @@ class DragState {
try (final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
transaction.setPosition(
mSurfaceControl,
- (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_X),
- (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_Y));
+ (float) animation.getAnimatedValue(ANIMATED_PROPERTY_X),
+ (float) animation.getAnimatedValue(ANIMATED_PROPERTY_Y));
transaction.setAlpha(
mSurfaceControl,
- (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_ALPHA));
+ (float) animation.getAnimatedValue(ANIMATED_PROPERTY_ALPHA));
transaction.setMatrix(
mSurfaceControl,
- (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0,
- 0, (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE));
+ (float) animation.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0,
+ 0, (float) animation.getAnimatedValue(ANIMATED_PROPERTY_SCALE));
transaction.apply();
}
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 5d4ba0992f16..87d0a4016634 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -382,6 +382,27 @@ class TaskPositioner {
mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
mWindowOriginalBounds.set(startBounds);
+ // Notify the app that resizing has started, even though we haven't received any new
+ // bounds yet. This will guarantee that the app starts the backdrop renderer before
+ // configuration changes which could cause an activity restart.
+ if (mResizing) {
+ synchronized (mService.mWindowMap) {
+ notifyMoveLocked(startX, startY);
+ }
+
+ // Perform the resize on the WMS handler thread when we don't have the WMS lock held
+ // to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver
+ // callbacks are delivered on the same handler so this initial resize is always
+ // guaranteed to happen before subsequent drag resizes.
+ mService.mH.post(() -> {
+ try {
+ mService.mActivityManager.resizeTask(
+ mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
+ } catch (RemoteException e) {
+ }
+ });
+ }
+
// Make sure we always have valid drag bounds even if the drag ends before any move events
// have been handled.
mWindowDragBounds.set(startBounds);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e6444179d13c..43a089373a35 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -18,12 +18,12 @@ package com.android.server.wm;
import static com.android.server.wm.TaskSnapshotPersister.DISABLE_FULL_SIZED_BITMAPS;
import static com.android.server.wm.TaskSnapshotPersister.REDUCED_SCALE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@@ -35,6 +35,7 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.DisplayListCanvas;
import android.view.RenderNode;
+import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
import android.view.WindowManager.LayoutParams;
@@ -210,11 +211,28 @@ class TaskSnapshotController {
if (mainWindow == null) {
return null;
}
+ if (!mService.mPolicy.isScreenOn()) {
+ if (DEBUG_SCREENSHOT) {
+ Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
+ }
+ return null;
+ }
+ if (task.getSurfaceControl() == null) {
+ return null;
+ }
+
final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
final float scaleFraction = isLowRamDevice ? REDUCED_SCALE : 1f;
- final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
- -1, -1, false, scaleFraction, false, true);
+ final Rect taskFrame = new Rect();
+ task.getBounds(taskFrame);
+
+ final GraphicBuffer buffer = SurfaceControl.captureLayers(
+ task.getSurfaceControl().getHandle(), taskFrame, scaleFraction);
+
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
+ if (DEBUG_SCREENSHOT) {
+ Slog.w(TAG_WM, "Failed to take screenshot");
+ }
return null;
}
return new TaskSnapshot(buffer, top.getConfiguration().orientation,
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 610453e6e92b..64675825243e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -731,29 +731,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
final WindowContainer p = getParent();
// Give the parent a chance to set properties. In hierarchy v1 we rely
// on this to set full-screen dimensions on all our Surface-less Layers.
- final SurfaceControl.Builder b = p.makeChildSurface(child);
- if (child != null && child.isScreenOverlay()) {
- // If it's a screen overlay it's been promoted in the hierarchy (wrt to the
- // WindowContainer hierarchy vs the SurfaceControl hierarchy)
- // and we shouldn't set ourselves as the parent.
- return b;
- } else {
- return b.setParent(mSurfaceControl);
- }
- }
-
- /**
- * There are various layers which require promotion from the WindowContainer
- * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState}
- * for the particular usage.
- *
- * TODO: Perhaps this should be eliminated, either through modifying
- * the window container hierarchy or through modifying the way we express these overlay
- * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode
- * Flash and not actually use a WindowState).
- */
- boolean isScreenOverlay() {
- return false;
+ return p.makeChildSurface(child)
+ .setParent(mSurfaceControl);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1cd40806619f..82f842c0fd85 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3691,9 +3691,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
- return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */,
- -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */,
- Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */);
+ return screenshotApplications(DEFAULT_DISPLAY, Bitmap.Config.ARGB_8888,
+ true /* wallpaperOnly */);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -3712,10 +3711,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
FgThread.getHandler().post(() -> {
- Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY,
- -1 /* width */, -1 /* height */, true /* includeFullDisplay */,
- 1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */,
- false /* includeDecor */);
+ Bitmap bm = screenshotApplications(DEFAULT_DISPLAY, Bitmap.Config.ARGB_8888,
+ false /* wallpaperOnly */);
try {
receiver.onHandleAssistScreenshot(bm);
} catch (RemoteException e) {
@@ -3749,29 +3746,21 @@ public class WindowManagerService extends IWindowManager.Stub
* In portrait mode, it grabs the full screenshot.
*
* @param displayId the Display to take a screenshot of.
- * @param width the width of the target bitmap
- * @param height the height of the target bitmap
- * @param includeFullDisplay true if the screen should not be cropped before capture
- * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
* @param config of the output bitmap
* @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
- * @param includeDecor whether to include window decors, like the status or navigation bar
- * background of the window
*/
- private Bitmap screenshotApplications(IBinder appToken, int displayId, int width,
- int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config,
- boolean wallpaperOnly, boolean includeDecor) {
+ private Bitmap screenshotApplications(int displayId, Bitmap.Config config,
+ boolean wallpaperOnly) {
final DisplayContent displayContent;
synchronized(mWindowMap) {
displayContent = mRoot.getDisplayContentOrCreate(displayId);
if (displayContent == null) {
- if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
- + ": returning null. No Display for displayId=" + displayId);
+ if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot returning null. No Display for "
+ + "displayId=" + displayId);
return null;
}
}
- return displayContent.screenshotApplications(appToken, width, height,
- includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor);
+ return displayContent.screenshotDisplay(config, wallpaperOnly);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 90e9c230b501..c2ac905219ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -58,6 +58,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -4110,9 +4111,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top,
displayInfo.logicalWidth - mCompatFrame.left,
displayInfo.logicalHeight - mCompatFrame.top);
- } else if (mLayer >= mService.mSystemDecorLayer) {
- // Above the decor layer is easy, just use the entire window
- policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
} else if (mDecorFrame.isEmpty()) {
// Windows without policy decor aren't cropped.
policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
@@ -4346,24 +4344,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
boolean shouldMagnify() {
if (mAttrs.type == TYPE_INPUT_METHOD ||
- mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
- return false;
- } else if (isScreenOverlay()) {
- return false;
- }
- return true;
- }
-
- @Override
- boolean isScreenOverlay() {
- // It's tempting to wonder: Have we forgotten the rounded corners overlay?
- // worry not: it's a fake TYPE_NAVIGATION_BAR.
- if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
+ mAttrs.type == TYPE_INPUT_METHOD_DIALOG ||
+ mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
mAttrs.type == TYPE_NAVIGATION_BAR ||
+ // It's tempting to wonder: Have we forgotten the rounded corners overlay?
+ // worry not: it's a fake TYPE_NAVIGATION_BAR_PANEL
+ mAttrs.type == TYPE_NAVIGATION_BAR_PANEL ||
mAttrs.type == TYPE_STATUS_BAR) {
- return true;
+ return false;
}
- return false;
+ return true;
}
@Override
@@ -4382,7 +4372,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
SurfaceControl.Builder makeSurface() {
- return mToken.makeChildSurface(this);
+ return getParent().makeChildSurface(this);
}
private void applyDims(Dimmer dimmer) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d2b37481a429..4b5661f5d04d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -871,11 +871,9 @@ class WindowStateAnimator {
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;
- Transformation attachedTransformation =
- (mParentWinAnimator != null && mParentWinAnimator.mHasLocalTransformation)
- ? mParentWinAnimator.mTransformation : null;
Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation)
? mAppAnimator.transformation : null;
+ Transformation wallpaperTargetTransformation = null;
// Wallpapers are animated based on the "real" window they
// are currently targeting.
@@ -885,9 +883,9 @@ class WindowStateAnimator {
if (wallpaperAnimator.mHasLocalTransformation &&
wallpaperAnimator.mAnimation != null &&
!wallpaperAnimator.mAnimation.getDetachWallpaper()) {
- attachedTransformation = wallpaperAnimator.mTransformation;
- if (DEBUG_WALLPAPER && attachedTransformation != null) {
- Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
+ wallpaperTargetTransformation = wallpaperAnimator.mTransformation;
+ if (DEBUG_WALLPAPER && wallpaperTargetTransformation != null) {
+ Slog.v(TAG, "WP target attached xform: " + wallpaperTargetTransformation);
}
}
final AppWindowAnimator wpAppAnimator = wallpaperTarget.mAppToken == null ?
@@ -909,7 +907,7 @@ class WindowStateAnimator {
screenRotationAnimation != null && screenRotationAnimation.isAnimating();
mHasClipRect = false;
- if (selfTransformation || attachedTransformation != null
+ if (selfTransformation || wallpaperTargetTransformation != null
|| appTransformation != null || screenAnimation) {
// cache often used attributes locally
final Rect frame = mWin.mFrame;
@@ -939,18 +937,27 @@ class WindowStateAnimator {
if (selfTransformation) {
tmpMatrix.postConcat(mTransformation.getMatrix());
}
- if (attachedTransformation != null) {
- tmpMatrix.postConcat(attachedTransformation.getMatrix());
+
+ if (wallpaperTargetTransformation != null) {
+ tmpMatrix.postConcat(wallpaperTargetTransformation.getMatrix());
}
if (appTransformation != null) {
tmpMatrix.postConcat(appTransformation.getMatrix());
}
+ int left = frame.left;
+ int top = frame.top;
+ if (mWin.isChildWindow()) {
+ WindowState parent = mWin.getParentWindow();
+ left -= parent.mFrame.left;
+ top -= parent.mFrame.top;
+ }
+
// The translation that applies the position of the window needs to be applied at the
// end in case that other translations include scaling. Otherwise the scaling will
// affect this translation. But it needs to be set before the screen rotation animation
// so the pivot point is at the center of the screen for all windows.
- tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
+ tmpMatrix.postTranslate(left + mWin.mXOffset, top + mWin.mYOffset);
if (screenAnimation) {
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
@@ -985,8 +992,8 @@ class WindowStateAnimator {
if (selfTransformation) {
mShownAlpha *= mTransformation.getAlpha();
}
- if (attachedTransformation != null) {
- mShownAlpha *= attachedTransformation.getAlpha();
+ if (wallpaperTargetTransformation != null) {
+ mShownAlpha *= wallpaperTargetTransformation.getAlpha();
}
if (appTransformation != null) {
mShownAlpha *= appTransformation.getAlpha();
@@ -1017,8 +1024,8 @@ class WindowStateAnimator {
&& (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
+ " self=" + (selfTransformation ? mTransformation.getAlpha() : "null")
- + " attached=" + (attachedTransformation == null ?
- "null" : attachedTransformation.getAlpha())
+ + " attached=" + (wallpaperTargetTransformation == null ?
+ "null" : wallpaperTargetTransformation.getAlpha())
+ " app=" + (appTransformation == null ? "null" : appTransformation.getAlpha())
+ " screen=" + (screenAnimation ?
screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index df85e318619a..35e65bc3110f 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -17,16 +17,18 @@
#define LOG_TAG "UsbHostManagerJNI"
#include "utils/Log.h"
+#include <stdlib.h>
+
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <usbhost/usbhost.h>
-#define MAX_DESCRIPTORS_LENGTH 16384
+#define MAX_DESCRIPTORS_LENGTH 4096
// com.android.server.usb.descriptors
extern "C" {
-jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors(
+jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors_1native(
JNIEnv* env, jobject thiz, jstring deviceAddr) {
const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
struct usb_device* device = usb_device_open(deviceAddrStr);
@@ -39,6 +41,7 @@ jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_g
int fd = usb_device_get_fd(device);
if (fd < 0) {
+ usb_device_close(device);
return NULL;
}
@@ -46,17 +49,48 @@ jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_g
jbyte buffer[MAX_DESCRIPTORS_LENGTH];
lseek(fd, 0, SEEK_SET);
int numBytes = read(fd, buffer, sizeof(buffer));
-
+ jbyteArray ret = NULL;
usb_device_close(device);
- jbyteArray ret = NULL;
- if (numBytes != 0) {
+ if (numBytes > 0) {
ret = env->NewByteArray(numBytes);
env->SetByteArrayRegion(ret, 0, numBytes, buffer);
+ } else {
+ ALOGE("error reading descriptors\n");
}
+
return ret;
}
+jstring JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getDescriptorString_1native(
+ JNIEnv* env, jobject thiz, jstring deviceAddr, jint stringId) {
+
+ const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
+ struct usb_device* device = usb_device_open(deviceAddrStr);
+ env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr);
+
+ if (!device) {
+ ALOGE("usb_device_open failed");
+ return NULL;
+ }
+
+ int fd = usb_device_get_fd(device);
+ if (fd < 0) {
+ ALOGE("usb_device_get_fd failed");
+ usb_device_close(device);
+ return NULL;
+ }
+
+ char* c_str = usb_device_get_string(device, stringId, 0 /*timeout*/);
+
+ jstring j_str = env->NewStringUTF(c_str);
+
+ free(c_str);
+ usb_device_close(device);
+
+ return j_str;
+}
+
} // extern "C"
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index 88ae824c1447..24f2014afa30 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -22,8 +22,6 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
-#include <usbhost/usbhost.h>
-
#include <stdio.h>
#include <asm/byteorder.h>
#include <sys/types.h>
@@ -31,22 +29,20 @@
#include <fcntl.h>
#include <sys/ioctl.h>
+#include <usbhost/usbhost.h>
+
+#define MAX_DESCRIPTORS_LENGTH 4096
+
namespace android
{
-static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
-
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
} gParcelFileDescriptorOffsets;
-static jmethodID method_beginUsbDeviceAdded;
-static jmethodID method_addUsbConfiguration;
-static jmethodID method_addUsbInterface;
-static jmethodID method_addUsbEndpoint;
-static jmethodID method_endUsbDeviceAdded;
+static jmethodID method_usbDeviceAdded;
static jmethodID method_usbDeviceRemoved;
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -57,101 +53,52 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa
}
}
-static int usb_device_added(const char *devname, void* client_data) {
- struct usb_descriptor_header* desc;
- struct usb_descriptor_iter iter;
-
- struct usb_device *device = usb_device_open(devname);
+static int usb_device_added(const char *devAddress, void* clientData) {
+ struct usb_device *device = usb_device_open(devAddress);
if (!device) {
ALOGE("usb_device_open failed\n");
return 0;
}
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject thiz = (jobject)client_data;
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
-
- char *manufacturer = usb_device_get_manufacturer_name(device,
- USB_CONTROL_TRANSFER_TIMEOUT_MS);
- char *product = usb_device_get_product_name(device,
- USB_CONTROL_TRANSFER_TIMEOUT_MS);
- int version = usb_device_get_version(device);
- char *serial = usb_device_get_serial(device,
- USB_CONTROL_TRANSFER_TIMEOUT_MS);
-
- jstring deviceName = env->NewStringUTF(devname);
- jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
- jstring productName = AndroidRuntime::NewStringLatin1(env, product);
- jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial);
-
- jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
- deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device),
- deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol,
- manufacturerName, productName, version, serialNumber);
-
- env->DeleteLocalRef(serialNumber);
- env->DeleteLocalRef(productName);
- env->DeleteLocalRef(manufacturerName);
- env->DeleteLocalRef(deviceName);
- free(manufacturer);
- free(product);
- free(serial);
-
- if (!result) goto fail;
-
- usb_descriptor_iter_init(device, &iter);
-
- while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
- if (desc->bDescriptorType == USB_DT_CONFIG) {
- struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
- char *name = usb_device_get_string(device, config->iConfiguration,
- USB_CONTROL_TRANSFER_TIMEOUT_MS);
- jstring configName = AndroidRuntime::NewStringLatin1(env, name);
-
- env->CallVoidMethod(thiz, method_addUsbConfiguration,
- config->bConfigurationValue, configName, config->bmAttributes,
- config->bMaxPower);
-
- env->DeleteLocalRef(configName);
- free(name);
- } else if (desc->bDescriptorType == USB_DT_INTERFACE) {
- struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
- char *name = usb_device_get_string(device, interface->iInterface,
- USB_CONTROL_TRANSFER_TIMEOUT_MS);
- jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name);
-
- env->CallVoidMethod(thiz, method_addUsbInterface,
- interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting,
- interface->bInterfaceClass, interface->bInterfaceSubClass,
- interface->bInterfaceProtocol);
-
- env->DeleteLocalRef(interfaceName);
- free(name);
- } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
- struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
-
- env->CallVoidMethod(thiz, method_addUsbEndpoint,
- endpoint->bEndpointAddress, endpoint->bmAttributes,
- __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval);
- }
+ int classID = deviceDesc->bDeviceClass;
+ int subClassID = deviceDesc->bDeviceSubClass;
+
+ // get the raw descriptors
+ int numBytes = usb_device_get_descriptors_length(device);
+ if (numBytes > 0) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject thiz = (jobject)clientData;
+ jstring deviceAddress = env->NewStringUTF(devAddress);
+
+ jbyteArray descriptorsArray = env->NewByteArray(numBytes);
+ const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
+ env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
+
+ env->CallBooleanMethod(thiz, method_usbDeviceAdded,
+ deviceAddress, classID, subClassID, descriptorsArray);
+
+ env->DeleteLocalRef(descriptorsArray);
+ env->DeleteLocalRef(deviceAddress);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ } else {
+ // TODO return an error code here?
+ ALOGE("error reading descriptors\n");
}
- env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
-
-fail:
usb_device_close(device);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
-static int usb_device_removed(const char *devname, void* client_data) {
+static int usb_device_removed(const char *devAddress, void* clientData) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject thiz = (jobject)client_data;
+ jobject thiz = (jobject)clientData;
- jstring deviceName = env->NewStringUTF(devname);
- env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName);
- env->DeleteLocalRef(deviceName);
+ jstring deviceAddress = env->NewStringUTF(devAddress);
+ env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
+ env->DeleteLocalRef(deviceAddress);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
@@ -168,11 +115,11 @@ static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, j
}
static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */,
- jstring deviceName)
+ jstring deviceAddress)
{
- const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
- struct usb_device* device = usb_device_open(deviceNameStr);
- env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+ const char *deviceAddressStr = env->GetStringUTFChars(deviceAddress, NULL);
+ struct usb_device* device = usb_device_open(deviceAddressStr);
+ env->ReleaseStringUTFChars(deviceAddress, deviceAddressStr);
if (!device)
return NULL;
@@ -206,34 +153,12 @@ int register_android_server_UsbHostManager(JNIEnv *env)
ALOGE("Can't find com/android/server/usb/UsbHostManager");
return -1;
}
- method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded",
- "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z");
- if (method_beginUsbDeviceAdded == NULL) {
+ method_usbDeviceAdded =
+ env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;II[B)Z");
+ if (method_usbDeviceAdded == NULL) {
ALOGE("Can't find beginUsbDeviceAdded");
return -1;
}
- method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration",
- "(ILjava/lang/String;II)V");
- if (method_addUsbConfiguration == NULL) {
- ALOGE("Can't find addUsbConfiguration");
- return -1;
- }
- method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface",
- "(ILjava/lang/String;IIII)V");
- if (method_addUsbInterface == NULL) {
- ALOGE("Can't find addUsbInterface");
- return -1;
- }
- method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V");
- if (method_addUsbEndpoint == NULL) {
- ALOGE("Can't find addUsbEndpoint");
- return -1;
- }
- method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V");
- if (method_endUsbDeviceAdded == NULL) {
- ALOGE("Can't find endUsbDeviceAdded");
- return -1;
- }
method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
"(Ljava/lang/String;)V");
if (method_usbDeviceRemoved == NULL) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index 608635491849..4a6bee5c7365 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -64,6 +64,8 @@ final class NetworkLoggingHandler extends Handler {
private final DevicePolicyManagerService mDpm;
private final AlarmManager mAlarmManager;
+ private long mId;
+
private final OnAlarmListener mBatchTimeoutAlarmListener = new OnAlarmListener() {
@Override
public void onAlarm() {
@@ -185,6 +187,10 @@ final class NetworkLoggingHandler extends Handler {
private Bundle finalizeBatchAndBuildDeviceOwnerMessageLocked() {
Bundle notificationExtras = null;
if (mNetworkEvents.size() > 0) {
+ // Assign ids to the events.
+ for (NetworkEvent event : mNetworkEvents) {
+ event.setId(mId++);
+ }
// Finalize the batch and start a new one from scratch.
if (mBatches.size() >= MAX_BATCHES) {
// Remove the oldest batch if we hit the limit.
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index a2ec23496a03..e5ab44ffaf6b 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -26,6 +26,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
platform-test-annotations \
ShortcutManagerTestUtils \
truth-prebuilt \
+ testables \
testng
LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 9a5ebedcf361..0499bf0eccc7 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -58,6 +58,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index b4919b6f9c52..1cec0d93cabe 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -277,4 +277,10 @@ public class ActivityStarterTests extends ActivityTestsBase {
verify(options, times(1)).abort();
}
}
+
+// TODO(b/69270257): Add test to verify task layout is passed additional data such as activity and
+// source.
+// @Test
+// public void testCreateTaskLayout() {
+// }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 2fffb892c5f0..d74d994844ef 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -191,6 +191,7 @@ public class ActivityTestsBase {
private String mPackage;
private int mFlags = 0;
private int mTaskId = 0;
+ private int mUserId = 0;
private IVoiceInteractionSession mVoiceSession;
private ActivityStack mStack;
@@ -224,6 +225,11 @@ public class ActivityTestsBase {
return this;
}
+ TaskBuilder setUserId(int userId) {
+ mUserId = userId;
+ return this;
+ }
+
TaskBuilder setStack(ActivityStack stack) {
mStack = stack;
return this;
@@ -245,10 +251,12 @@ public class ActivityTestsBase {
final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
+ task.userId = mUserId;
mSupervisor.setFocusStackUnchecked("test", mStack);
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
task.setWindowContainerController(mock(TaskWindowContainerController.class));
+ task.touchActiveTime();
return task;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index afece5d21940..13ca10c545ca 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -30,6 +31,8 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static java.lang.Integer.MAX_VALUE;
@@ -42,6 +45,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -64,6 +68,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Random;
import java.util.Set;
/**
@@ -78,17 +83,19 @@ public class RecentTasksTest extends ActivityTestsBase {
private static final int TEST_QUIET_USER_ID = 20;
private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
private static final UserInfo QUIET_USER_INFO = new UserInfo();
- private static final ComponentName MY_COMPONENT = new ComponentName(
- RecentTasksTest.class.getPackage().getName(), RecentTasksTest.class.getName());
private static int LAST_TASK_ID = 1;
+ private static int LAST_STACK_ID = 1;
private static int INVALID_STACK_ID = 999;
private Context mContext = InstrumentationRegistry.getContext();
private ActivityManagerService mService;
+ private ActivityDisplay mDisplay;
+ private ActivityDisplay mOtherDisplay;
private ActivityStack mStack;
+ private ActivityStack mHomeStack;
private TestTaskPersister mTaskPersister;
- private RecentTasks mRecentTasks;
- private RunningTasks mRunningTasks;
+ private TestRecentTasks mRecentTasks;
+ private TestRunningTasks mRunningTasks;
private static ArrayList<TaskRecord> mTasks = new ArrayList<>();
private static ArrayList<TaskRecord> mSameDocumentTasks = new ArrayList<>();
@@ -133,22 +140,25 @@ public class RecentTasksTest extends ActivityTestsBase {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
mService = setupActivityManagerService(new MyTestActivityManagerService(mContext));
- mRecentTasks = mService.getRecentTasks();
+ mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
+ mHomeStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ ((MyTestActivityStackSupervisor) mService.mStackSupervisor).setHomeStack(mHomeStack);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
- mTasks.add(createTask(".Task1"));
- mTasks.add(createTask(".Task2"));
- mTasks.add(createTask(".Task3"));
- mTasks.add(createTask(".Task4"));
- mTasks.add(createTask(".Task5"));
+ mTasks.add(createTaskBuilder(".Task1").build());
+ mTasks.add(createTaskBuilder(".Task2").build());
+ mTasks.add(createTaskBuilder(".Task3").build());
+ mTasks.add(createTaskBuilder(".Task4").build());
+ mTasks.add(createTaskBuilder(".Task5").build());
- mSameDocumentTasks.add(createDocumentTask(".DocumentTask1", null /* affinity */));
- mSameDocumentTasks.add(createDocumentTask(".DocumentTask1", null /* affinity */));
+ mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
+ mSameDocumentTasks.add(createDocumentTask(".DocumentTask1"));
}
@Test
@@ -172,9 +182,9 @@ public class RecentTasksTest extends ActivityTestsBase {
mCallbacksRecorder.clear();
// Add a task which will trigger the trimming of another
- TaskRecord documentTask1 = createDocumentTask(".DocumentTask1", null /* affinity */);
+ TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
documentTask1.maxRecents = 1;
- TaskRecord documentTask2 = createDocumentTask(".DocumentTask1", null /* affinity */);
+ TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
mRecentTasks.add(documentTask1);
mRecentTasks.add(documentTask2);
assertTrue(mCallbacksRecorder.added.contains(documentTask1));
@@ -194,13 +204,15 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testUsersTasks() throws Exception {
+ mRecentTasks.setOnlyTestVisibleRange();
+
// Setup some tasks for the users
mTaskPersister.userTaskIdsOverride = new SparseBooleanArray();
mTaskPersister.userTaskIdsOverride.put(1, true);
mTaskPersister.userTaskIdsOverride.put(2, true);
mTaskPersister.userTasksOverride = new ArrayList<>();
- mTaskPersister.userTasksOverride.add(createTask(".UserTask1"));
- mTaskPersister.userTasksOverride.add(createTask(".UserTask2"));
+ mTaskPersister.userTasksOverride.add(createTaskBuilder(".UserTask1").build());
+ mTaskPersister.userTasksOverride.add(createTaskBuilder(".UserTask2").build());
// Assert no user tasks are initially loaded
assertTrue(mRecentTasks.usersWithRecentsLoadedLocked().length == 0);
@@ -235,6 +247,20 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testOrderedIteration() throws Exception {
+ mRecentTasks.setOnlyTestVisibleRange();
+ TaskRecord task1 = createTaskBuilder(".Task1").build();
+ task1.lastActiveTime = new Random().nextInt();
+ TaskRecord task2 = createTaskBuilder(".Task1").build();
+ task2.lastActiveTime = new Random().nextInt();
+ TaskRecord task3 = createTaskBuilder(".Task1").build();
+ task3.lastActiveTime = new Random().nextInt();
+ TaskRecord task4 = createTaskBuilder(".Task1").build();
+ task4.lastActiveTime = new Random().nextInt();
+ mRecentTasks.add(task1);
+ mRecentTasks.add(task2);
+ mRecentTasks.add(task3);
+ mRecentTasks.add(task4);
+
MutableLong prevLastActiveTime = new MutableLong(0);
final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
for (int i = 0; i < tasks.size(); i++) {
@@ -246,6 +272,8 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testTrimToGlobalMaxNumRecents() throws Exception {
+ mRecentTasks.setOnlyTestVisibleRange();
+
// Limit the global maximum number of recent tasks to a fixed size
mRecentTasks.setGlobalMaxNumTasks(2 /* globalMaxNumTasks */);
@@ -260,8 +288,9 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testTrimQuietProfileTasks() throws Exception {
- TaskRecord qt1 = createTask(".QuietTask1", TEST_QUIET_USER_ID);
- TaskRecord qt2 = createTask(".QuietTask2", TEST_QUIET_USER_ID);
+ mRecentTasks.setOnlyTestVisibleRange();
+ TaskRecord qt1 = createTaskBuilder(".QuietTask1").setUserId(TEST_QUIET_USER_ID).build();
+ TaskRecord qt2 = createTaskBuilder(".QuietTask2").setUserId(TEST_QUIET_USER_ID).build();
mRecentTasks.add(qt1);
mRecentTasks.add(qt2);
@@ -274,16 +303,17 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testSessionDuration() throws Exception {
+ mRecentTasks.setOnlyTestVisibleRange();
mRecentTasks.setParameters(-1 /* min */, -1 /* max */, 50 /* ms */);
- TaskRecord t1 = createTask(".Task1");
+ TaskRecord t1 = createTaskBuilder(".Task1").build();
t1.touchActiveTime();
mRecentTasks.add(t1);
// Force a small sleep just beyond the session duration
SystemClock.sleep(75);
- TaskRecord t2 = createTask(".Task2");
+ TaskRecord t2 = createTaskBuilder(".Task2").build();
t2.touchActiveTime();
mRecentTasks.add(t2);
@@ -293,12 +323,15 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testVisibleTasks_excludedFromRecents() throws Exception {
+ mRecentTasks.setOnlyTestVisibleRange();
mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
- TaskRecord excludedTask1 = createTask(".ExcludedTask1", FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
- TEST_USER_0_ID);
- TaskRecord excludedTask2 = createTask(".ExcludedTask2", FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
- TEST_USER_0_ID);
+ TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1")
+ .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ .build();
+ TaskRecord excludedTask2 = createTaskBuilder(".ExcludedTask2")
+ .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ .build();
mRecentTasks.add(excludedTask1);
mRecentTasks.add(mTasks.get(0));
@@ -312,6 +345,7 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testVisibleTasks_minNum() throws Exception {
+ mRecentTasks.setOnlyTestVisibleRange();
mRecentTasks.setParameters(5 /* min */, -1 /* max */, 25 /* ms */);
for (int i = 0; i < 4; i++) {
@@ -327,12 +361,12 @@ public class RecentTasksTest extends ActivityTestsBase {
mRecentTasks.add(mTasks.get(4));
// Ensure that there are a minimum number of tasks regardless of session length
- assertTrue(mCallbacksRecorder.trimmed.isEmpty());
- assertTrue(mCallbacksRecorder.removed.isEmpty());
+ assertNoTasksTrimmed();
}
@Test
public void testVisibleTasks_maxNum() throws Exception {
+ mRecentTasks.setOnlyTestVisibleRange();
mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
for (int i = 0; i < 5; i++) {
@@ -346,16 +380,78 @@ public class RecentTasksTest extends ActivityTestsBase {
}
@Test
+ public void testBackStackTasks_expectNoTrim() throws Exception {
+ mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
+
+ final MyTestActivityStackSupervisor supervisor =
+ (MyTestActivityStackSupervisor) mService.mStackSupervisor;
+ final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ supervisor.setHomeStack(homeStack);
+
+ // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
+ // the tasks belong in stacks above the home stack
+ mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
+ mRecentTasks.add(createTaskBuilder(".Task1").setStack(aboveHomeStack).build());
+ mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
+ mRecentTasks.add(createTaskBuilder(".Task3").setStack(aboveHomeStack).build());
+
+ assertNoTasksTrimmed();
+ }
+
+ @Test
+ public void testBehindHomeStackTasks_expectTaskTrimmed() throws Exception {
+ mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
+
+ final MyTestActivityStackSupervisor supervisor =
+ (MyTestActivityStackSupervisor) mService.mStackSupervisor;
+ final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ supervisor.setHomeStack(homeStack);
+
+ // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
+ // the home stack is trimmed once a new task is added
+ final TaskRecord behindHomeTask = createTaskBuilder(".Task1")
+ .setStack(behindHomeStack)
+ .build();
+ mRecentTasks.add(behindHomeTask);
+ mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
+ mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
+
+ assertTrimmed(behindHomeTask);
+ }
+
+ @Test
+ public void testOtherDisplayTasks_expectNoTrim() throws Exception {
+ mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
+
+ final MyTestActivityStackSupervisor supervisor =
+ (MyTestActivityStackSupervisor) mService.mStackSupervisor;
+ final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
+ supervisor.setHomeStack(homeStack);
+
+ // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
+ // removed
+ mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
+ mRecentTasks.add(createTaskBuilder(".Task1").setStack(otherDisplayStack).build());
+ mRecentTasks.add(createTaskBuilder(".Task2").setStack(otherDisplayStack).build());
+ mRecentTasks.add(createTaskBuilder(".HomeTask2").setStack(homeStack).build());
+
+ assertNoTasksTrimmed();
+ }
+
+ @Test
public void testNotRecentsComponent_denyApiAccess() throws Exception {
doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
anyInt(), anyInt());
// Expect the following methods to fail due to recents component not being set
- ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(
- TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
+ mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
testRecentTasksApis(false /* expectNoSecurityException */);
// Don't throw for the following tests
- ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(TestRecentTasks.DENY);
+ mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY);
testGetTasksApis(false /* expectNoSecurityException */);
}
@@ -365,7 +461,7 @@ public class RecentTasksTest extends ActivityTestsBase {
anyInt(), anyInt());
// Set the recents component and ensure that the following calls do not fail
- ((TestRecentTasks) mRecentTasks).setIsCallerRecentsOverride(TestRecentTasks.GRANT);
+ mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
testRecentTasksApis(true /* expectNoSecurityException */);
testGetTasksApis(true /* expectNoSecurityException */);
}
@@ -436,38 +532,27 @@ public class RecentTasksTest extends ActivityTestsBase {
mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
mService.getTasks(MAX_VALUE);
if (expectCallable) {
- assertTrue(((TestRecentTasks) mRecentTasks).mLastAllowed);
- assertTrue(((TestRunningTasks) mRunningTasks).mLastAllowed);
+ assertTrue(mRecentTasks.lastAllowed);
+ assertTrue(mRunningTasks.lastAllowed);
} else {
- assertFalse(((TestRecentTasks) mRecentTasks).mLastAllowed);
- assertFalse(((TestRunningTasks) mRunningTasks).mLastAllowed);
+ assertFalse(mRecentTasks.lastAllowed);
+ assertFalse(mRunningTasks.lastAllowed);
}
}
- private ComponentName createComponent(String className) {
- return new ComponentName(mContext.getPackageName(), className);
- }
-
- private TaskRecord createTask(String className) {
- return createTask(className, TEST_USER_0_ID);
- }
-
- private TaskRecord createTask(String className, int userId) {
- return createTask(className, 0 /* flags */, userId);
- }
-
- private TaskRecord createTask(String className, int flags, int userId) {
- TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
- .setComponent(createComponent(className))
- .setStack(mStack).setFlags(flags).setTaskId(LAST_TASK_ID++).build();
- task.userId = userId;
- task.touchActiveTime();
- return task;
+ private TaskBuilder createTaskBuilder(String className) {
+ return new TaskBuilder(mService.mStackSupervisor)
+ .setComponent(new ComponentName(mContext.getPackageName(), className))
+ .setStack(mStack)
+ .setTaskId(LAST_TASK_ID++)
+ .setUserId(TEST_USER_0_ID);
}
- private TaskRecord createDocumentTask(String className, String affinity) {
- TaskRecord task = createTask(className, FLAG_ACTIVITY_NEW_DOCUMENT, TEST_USER_0_ID);
- task.affinity = affinity;
+ private TaskRecord createDocumentTask(String className) {
+ TaskRecord task = createTaskBuilder(className)
+ .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+ .build();
+ task.affinity = null;
return task;
}
@@ -476,6 +561,10 @@ public class RecentTasksTest extends ActivityTestsBase {
return Arrays.binarySearch(userIds, targetUserId) >= 0;
}
+ private void assertNoTasksTrimmed() {
+ assertTrimmed();
+ }
+
private void assertTrimmed(TaskRecord... tasks) {
final ArrayList<TaskRecord> trimmed = mCallbacksRecorder.trimmed;
final ArrayList<TaskRecord> removed = mCallbacksRecorder.removed;
@@ -532,10 +621,41 @@ public class RecentTasksTest extends ActivityTestsBase {
}
@Override
+ public void initialize() {
+ super.initialize();
+ mDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
+ mOtherDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
+ attachDisplay(mOtherDisplay);
+ attachDisplay(mDisplay);
+ }
+
+ @Override
RunningTasks createRunningTasks() {
mRunningTasks = new TestRunningTasks();
return mRunningTasks;
}
+
+ void setHomeStack(ActivityStack stack) {
+ mHomeStack = stack;
+ }
+ }
+
+ private class MyTestActivityStack extends TestActivityStack {
+ private ActivityDisplay mDisplay = null;
+
+ MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
+ super(display, LAST_STACK_ID++, supervisor, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true);
+ mDisplay = display;
+ }
+
+ @Override
+ ActivityDisplay getDisplay() {
+ if (mDisplay != null) {
+ return mDisplay;
+ }
+ return super.getDisplay();
+ }
}
private static class CallbacksRecorder implements Callbacks {
@@ -564,7 +684,6 @@ public class RecentTasksTest extends ActivityTestsBase {
}
private static class TestTaskPersister extends TaskPersister {
-
SparseBooleanArray userTaskIdsOverride;
ArrayList<TaskRecord> userTasksOverride;
@@ -595,8 +714,10 @@ public class RecentTasksTest extends ActivityTestsBase {
static final int DENY_THROW_SECURITY_EXCEPTION = 2;
private boolean mOverrideIsCallerRecents;
+ private boolean mIsTrimmableOverride;
private int mIsCallerRecentsPolicy;
- boolean mLastAllowed;
+
+ boolean lastAllowed;
TestRecentTasks(ActivityManagerService service, TaskPersister taskPersister,
UserController userController) {
@@ -623,24 +744,39 @@ public class RecentTasksTest extends ActivityTestsBase {
mIsCallerRecentsPolicy = policy;
}
+ /**
+ * To simplify the setup for some tests, the caller can request that we only rely on the
+ * visible range test to determine what is trimmable. In this case, we don't try to
+ * use the stack order to determine additionally if the task is trimmable when it is not
+ * in the visible range.
+ */
+ void setOnlyTestVisibleRange() {
+ mIsTrimmableOverride = true;
+ }
+
@Override
ParceledListSlice<RecentTaskInfo> getRecentTasks(int maxNum, int flags,
boolean getTasksAllowed,
boolean getDetailedTasks, int userId, int callingUid) {
- mLastAllowed = getTasksAllowed;
+ lastAllowed = getTasksAllowed;
return super.getRecentTasks(maxNum, flags, getTasksAllowed, getDetailedTasks, userId,
callingUid);
}
+
+ @Override
+ protected boolean isTrimmable(TaskRecord task) {
+ return mIsTrimmableOverride || super.isTrimmable(task);
+ }
}
private static class TestRunningTasks extends RunningTasks {
- boolean mLastAllowed;
+ boolean lastAllowed;
@Override
void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
int callingUid, boolean allowed) {
- mLastAllowed = allowed;
+ lastAllowed = allowed;
super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
callingUid, allowed);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
index db317a0e4cca..a92d1db2616b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
@@ -20,8 +20,6 @@ import android.app.admin.DnsEvent;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
-import static junit.framework.Assert.assertEquals;
-
@SmallTest
public class NetworkEventTest extends DpmTestBase {
@@ -30,6 +28,7 @@ public class NetworkEventTest extends DpmTestBase {
*/
public void testConnectEventParceling() {
ConnectEvent event = new ConnectEvent("127.0.0.1", 80, "com.android.whateverdude", 100000);
+ event.setId(5L);
Parcel p = Parcel.obtain();
p.writeParcelable(event, 0);
p.setDataPosition(0);
@@ -39,6 +38,7 @@ public class NetworkEventTest extends DpmTestBase {
assertEquals(event.getPort(), unparceledEvent.getPort());
assertEquals(event.getPackageName(), unparceledEvent.getPackageName());
assertEquals(event.getTimestamp(), unparceledEvent.getTimestamp());
+ assertEquals(event.getId(), unparceledEvent.getId());
}
/**
@@ -47,6 +47,7 @@ public class NetworkEventTest extends DpmTestBase {
public void testDnsEventParceling() {
DnsEvent event = new DnsEvent("d.android.com", new String[]{"192.168.0.1", "127.0.0.1"}, 2,
"com.android.whateverdude", 100000);
+ event.setId(5L);
Parcel p = Parcel.obtain();
p.writeParcelable(event, 0);
p.setDataPosition(0);
@@ -59,5 +60,6 @@ public class NetworkEventTest extends DpmTestBase {
unparceledEvent.getTotalResolvedAddressCount());
assertEquals(event.getPackageName(), unparceledEvent.getPackageName());
assertEquals(event.getTimestamp(), unparceledEvent.getTimestamp());
+ assertEquals(event.getId(), unparceledEvent.getId());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
new file mode 100644
index 000000000000..a70441d5b84b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -0,0 +1,252 @@
+/*
+ * 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.policy;
+
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.IApplicationToken;
+import android.view.WindowManager;
+
+public class FakeWindowState implements WindowManagerPolicy.WindowState {
+
+ public final Rect parentFrame = new Rect();
+ public final Rect displayFrame = new Rect();
+ public final Rect overscanFrame = new Rect();
+ public final Rect contentFrame = new Rect();
+ public final Rect visibleFrame = new Rect();
+ public final Rect decorFrame = new Rect();
+ public final Rect stableFrame = new Rect();
+ public Rect outsetFrame = new Rect();
+
+ public WindowManager.LayoutParams attrs;
+ public int displayId;
+ public boolean isVoiceInteraction;
+ public boolean inMultiWindowMode;
+ public boolean visible = true;
+ public int surfaceLayer = 1;
+
+ public boolean policyVisible = true;
+
+ @Override
+ public int getOwningUid() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public String getOwningPackage() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
+ Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
+ @Nullable Rect outsetFrame) {
+ this.parentFrame.set(parentFrame);
+ this.displayFrame.set(displayFrame);
+ this.overscanFrame.set(overlayFrame);
+ this.contentFrame.set(contentFrame);
+ this.visibleFrame.set(visibleFrame);
+ this.decorFrame.set(decorFrame);
+ this.stableFrame.set(stableFrame);
+ this.outsetFrame = outsetFrame == null ? null : new Rect(outsetFrame);
+ }
+
+ @Override
+ public Rect getFrameLw() {
+ return parentFrame;
+ }
+
+ @Override
+ public Point getShownPositionLw() {
+ return new Point(parentFrame.left, parentFrame.top);
+ }
+
+ @Override
+ public Rect getDisplayFrameLw() {
+ return displayFrame;
+ }
+
+ @Override
+ public Rect getOverscanFrameLw() {
+ return overscanFrame;
+ }
+
+ @Override
+ public Rect getContentFrameLw() {
+ return contentFrame;
+ }
+
+ @Override
+ public Rect getVisibleFrameLw() {
+ return visibleFrame;
+ }
+
+ @Override
+ public boolean getGivenInsetsPendingLw() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public Rect getGivenContentInsetsLw() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public Rect getGivenVisibleInsetsLw() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public WindowManager.LayoutParams getAttrs() {
+ return attrs;
+ }
+
+ @Override
+ public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public int getSystemUiVisibility() {
+ return attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
+ }
+
+ @Override
+ public int getSurfaceLayer() {
+ return surfaceLayer;
+ }
+
+ @Override
+ public int getBaseType() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public IApplicationToken getAppToken() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean isVoiceInteraction() {
+ return isVoiceInteraction;
+ }
+
+ @Override
+ public boolean hasAppShownWindows() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean isVisibleLw() {
+ return visible && policyVisible;
+ }
+
+ @Override
+ public boolean isDisplayedLw() {
+ return isVisibleLw();
+ }
+
+ @Override
+ public boolean isAnimatingLw() {
+ return false;
+ }
+
+ @Override
+ public boolean canAffectSystemUiFlags() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean isGoneForLayoutLw() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean isDrawnLw() {
+ return true;
+ }
+
+ @Override
+ public boolean hasDrawnLw() {
+ return true;
+ }
+
+ @Override
+ public boolean hideLw(boolean doAnimation) {
+ if (!policyVisible) {
+ return false;
+ }
+ policyVisible = false;
+ return true;
+ }
+
+ @Override
+ public boolean showLw(boolean doAnimation) {
+ if (policyVisible) {
+ return false;
+ }
+ policyVisible = true;
+ return true;
+ }
+
+ @Override
+ public boolean isAlive() {
+ return true;
+ }
+
+ @Override
+ public boolean isDefaultDisplay() {
+ return displayId == Display.DEFAULT_DISPLAY;
+ }
+
+ @Override
+ public boolean isDimming() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public int getWindowingMode() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean isInMultiWindowMode() {
+ return inMultiWindowMode;
+ }
+
+ @Override
+ public int getRotationAnimationHint() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public boolean isInputMethodWindow() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ public int getDisplayId() {
+ return displayId;
+ }
+
+ @Override
+ public boolean canAcquireSleepToken() {
+ throw new UnsupportedOperationException("not implemented");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
new file mode 100644
index 000000000000..0941d4fd4fc9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.policy;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.PixelFormat;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
+
+ private FakeWindowState mAppWindow;
+
+ @Before
+ public void setUp() throws Exception {
+ mAppWindow = new FakeWindowState();
+ mAppWindow.attrs = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
+ TYPE_APPLICATION,
+ FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ PixelFormat.TRANSLUCENT);
+
+ addStatusBar();
+ addNavigationBar();
+ }
+
+ @Test
+ public void layoutWindowLw_appDrawsBars() throws Exception {
+ mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mPolicy.addWindow(mAppWindow);
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+ assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
+ assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
+ }
+
+ @Test
+ public void layoutWindowLw_appWontDrawBars() throws Exception {
+ mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mPolicy.addWindow(mAppWindow);
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+ assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.decorFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ }
+
+ @Test
+ public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
+ mAppWindow.attrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+ mPolicy.addWindow(mAppWindow);
+
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+ assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.decorFrame, 0, NAV_BAR_HEIGHT);
+ }
+
+ @Test
+ public void addingWindow_doesNotTamperWithSysuiFlags() {
+ mAppWindow.attrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mPolicy.addWindow(mAppWindow);
+
+ assertEquals(0, mAppWindow.attrs.systemUiVisibility);
+ assertEquals(0, mAppWindow.attrs.subtreeSystemUiVisibility);
+ }
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
new file mode 100644
index 000000000000..9c5570725baa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -0,0 +1,189 @@
+/*
+ * 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.policy;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.testing.TestableResources;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+
+import com.android.server.policy.keyguard.KeyguardServiceDelegate;
+import com.android.server.wm.DisplayFrames;
+
+import org.junit.Before;
+
+public class PhoneWindowManagerTestBase {
+ static final int DISPLAY_WIDTH = 500;
+ static final int DISPLAY_HEIGHT = 1000;
+
+ static final int STATUS_BAR_HEIGHT = 10;
+ static final int NAV_BAR_HEIGHT = 15;
+
+ TestablePhoneWindowManager mPolicy;
+ TestContextWrapper mContext;
+ DisplayFrames mFrames;
+
+ FakeWindowState mStatusBar;
+ FakeWindowState mNavigationBar;
+
+ @Before
+ public void setUpBase() throws Exception {
+ mContext = new TestContextWrapper(InstrumentationRegistry.getTargetContext());
+ mContext.getResourceMocker().addOverride(
+ com.android.internal.R.dimen.status_bar_height, STATUS_BAR_HEIGHT);
+ mContext.getResourceMocker().addOverride(
+ com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT);
+ mContext.getResourceMocker().addOverride(
+ com.android.internal.R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT);
+ mContext.getResourceMocker().addOverride(
+ com.android.internal.R.dimen.navigation_bar_width, NAV_BAR_HEIGHT);
+
+ mPolicy = TestablePhoneWindowManager.create(mContext);
+
+ DisplayInfo info = new DisplayInfo();
+ info.logicalWidth = DISPLAY_WIDTH;
+ info.logicalHeight = DISPLAY_HEIGHT;
+ info.rotation = ROTATION_0;
+ mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info);
+ }
+
+ public void addStatusBar() {
+ mStatusBar = new FakeWindowState();
+ mStatusBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT,
+ TYPE_STATUS_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
+ mStatusBar.attrs.gravity = Gravity.TOP;
+
+ mPolicy.addWindow(mStatusBar);
+ mPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
+ }
+
+ public void addNavigationBar() {
+ mNavigationBar = new FakeWindowState();
+ mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT,
+ TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT);
+ mNavigationBar.attrs.gravity = Gravity.BOTTOM;
+
+ mPolicy.addWindow(mNavigationBar);
+ mPolicy.mHasNavigationBar = true;
+ mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
+ }
+
+ /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
+ public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
+ int expectedInsetRight, int expectedInsetBottom) {
+ assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
+ DISPLAY_WIDTH - expectedInsetRight, DISPLAY_HEIGHT - expectedInsetBottom), actual);
+ }
+
+ /**
+ * Asserts that {@code actual} is inset by the given amounts from the full display rect.
+ *
+ * Convenience wrapper for when only the top and bottom inset are non-zero.
+ */
+ public void assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom) {
+ assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
+ }
+
+ static class TestContextWrapper extends ContextWrapper {
+ private final TestableResources mResourceMocker;
+
+ public TestContextWrapper(Context targetContext) {
+ super(targetContext);
+ mResourceMocker = new TestableResources(targetContext.getResources());
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public Resources getResources() {
+ return mResourceMocker.getResources();
+ }
+
+ public TestableResources getResourceMocker() {
+ return mResourceMocker;
+ }
+ }
+
+ static class TestablePhoneWindowManager extends PhoneWindowManager {
+
+ public TestablePhoneWindowManager() {
+ }
+
+ @Override
+ void initializeHdmiState() {
+ // Do nothing.
+ }
+
+ @Override
+ Context getSystemUiContext() {
+ return mContext;
+ }
+
+ void addWindow(WindowState state) {
+ if (state instanceof FakeWindowState) {
+ ((FakeWindowState) state).surfaceLayer =
+ getWindowLayerFromTypeLw(state.getAttrs().type);
+ }
+ adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */);
+ assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs()));
+ }
+
+ public static TestablePhoneWindowManager create(Context context) {
+ TestablePhoneWindowManager[] policy = new TestablePhoneWindowManager[1];
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ policy[0] = new TestablePhoneWindowManager();
+ policy[0].mContext = context;
+ policy[0].mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
+ policy[0].mAccessibilityManager = new AccessibilityManager(context,
+ mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
+ policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
+ policy[0].onConfigurationChanged();
+ });
+ return policy[0];
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
index 7e2a7d221c22..7324fe6de9d8 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
@@ -15,8 +15,9 @@
*/
package com.android.server.power.batterysaver;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -26,6 +27,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.hardware.camera2.impl.GetCommand;
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
@@ -40,6 +42,7 @@ import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -72,6 +75,17 @@ public class FileUpdaterTest {
void injectWtf(String message, Throwable e) {
mInjector.injectWtf(message, e);
}
+
+ @Override
+ File injectDefaultValuesFilename() {
+ return new File(InstrumentationRegistry.getContext().getCacheDir() +
+ "/test-default.xml");
+ }
+
+ @Override
+ boolean injectShouldSkipWrite() {
+ return false;
+ }
}
private interface Injector {
@@ -334,4 +348,57 @@ public class FileUpdaterTest {
reset(mInjector);
testMultiWrites();
}
+
+ @Test
+ public void testWriteReadDefault() throws Exception {
+ doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
+ doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
+ doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
+
+ // Write
+ final ArrayMap<String, String> values = new ArrayMap<>();
+ values.put("file1", "11");
+ values.put("file2", "22");
+ values.put("file3", "33");
+
+ mInstance.writeFiles(values);
+ waitUntilMainHandlerDrain();
+
+ verify(mInjector, times(1)).injectWriteToFile("file1", "11");
+ verify(mInjector, times(1)).injectWriteToFile("file2", "22");
+ verify(mInjector, times(1)).injectWriteToFile("file3", "33");
+
+ // Clear and reload the default.
+ assertEquals(3, mInstance.getDefaultValuesForTest().size());
+ mInstance.getDefaultValuesForTest().clear();
+ assertEquals(0, mInstance.getDefaultValuesForTest().size());
+
+ mInstance.systemReady(/*runtimeRestarted=*/ true);
+
+ assertEquals(3, mInstance.getDefaultValuesForTest().size());
+
+ // Reset to default
+ mInstance.restoreDefault();
+ waitUntilMainHandlerDrain();
+
+ verify(mInjector, times(1)).injectWriteToFile("file1", "111");
+ verify(mInjector, times(1)).injectWriteToFile("file2", "222");
+ verify(mInjector, times(1)).injectWriteToFile("file3", "333");
+
+ // Make sure the default file still exists.
+ assertTrue(mInstance.injectDefaultValuesFilename().exists());
+
+ // Simulate a clean boot.
+ mInstance.getDefaultValuesForTest().clear();
+ assertEquals(0, mInstance.getDefaultValuesForTest().size());
+
+ mInstance.systemReady(/*runtimeRestarted=*/ false);
+
+ // Default is empty, and the file is gone.
+ assertEquals(0, mInstance.getDefaultValuesForTest().size());
+ assertFalse(mInstance.injectDefaultValuesFilename().exists());
+
+ // No WTF should have happened.
+ veriryWtf(0);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 503e1ac4563d..fdcf57b18241 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -23,6 +23,7 @@ import org.junit.runner.RunWith;
import android.app.ActivityManager.TaskDescription;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Debug;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -339,11 +340,8 @@ public class WindowFrameTests extends WindowTestsBase {
w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
w.calculatePolicyCrop(policyCrop);
- // If we were above system decor we wouldnt' get any cropping though
- w.mLayer = sWm.mSystemDecorLayer + 1;
- w.calculatePolicyCrop(policyCrop);
- assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
- w.mLayer = 1;
+ assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
+
dcf.setEmpty();
// Likewise with no decor frame we would get no crop
w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index f7c4b1f51c46..04f5e5e919aa 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -42,6 +42,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
/**
* Tests for the {@link WindowLayersController} class.
@@ -185,7 +188,6 @@ public class ZOrderingTests extends WindowTestsBase {
// target.
assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
@@ -324,4 +326,21 @@ public class ZOrderingTests extends WindowTestsBase {
assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
}
+
+ @Test
+ public void testAssignWindowLayers_ForSysUiPanels() throws Exception {
+ final WindowState navBarPanel =
+ createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel");
+ final WindowState statusBarPanel =
+ createWindow(null, TYPE_STATUS_BAR_PANEL, mDisplayContent, "StatusBarPanel");
+ final WindowState statusBarSubPanel =
+ createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel");
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows and below system windows if it is targeting an app
+ // window.
+ assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow);
+ assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow);
+ assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel);
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index d359b7045007..7bea8a11133b 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -255,6 +255,7 @@ public final class UsbAlsaManager {
}
private void alsaFileAdded(String name) {
+ Slog.i(TAG, "alsaFileAdded(" + name + ")");
int type = AlsaDevice.TYPE_UNKNOWN;
int card = -1, device = -1;
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 9bc9cd04957c..dd2e19297272 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -19,13 +19,8 @@ package com.android.server.usb;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
-import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbDeviceConnection;
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
@@ -37,7 +32,6 @@ import com.android.server.usb.descriptors.UsbDescriptorParser;
import com.android.server.usb.descriptors.report.TextReportCanvas;
import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -50,28 +44,23 @@ public class UsbHostManager {
private final Context mContext;
- // contains all connected USB devices
- private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
-
// USB busses to exclude from USB host support
private final String[] mHostBlacklist;
- private final Object mLock = new Object();
-
- private UsbDevice mNewDevice;
- private UsbConfiguration mNewConfiguration;
- private UsbInterface mNewInterface;
- private ArrayList<UsbConfiguration> mNewConfigurations;
- private ArrayList<UsbInterface> mNewInterfaces;
- private ArrayList<UsbEndpoint> mNewEndpoints;
-
private final UsbAlsaManager mUsbAlsaManager;
private final UsbSettingsManager mSettingsManager;
+ private final Object mLock = new Object();
@GuardedBy("mLock")
+ // contains all connected USB devices
+ private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
+
+ private Object mSettingsLock = new Object();
+ @GuardedBy("mSettingsLock")
private UsbProfileGroupSettingsManager mCurrentSettings;
- @GuardedBy("mLock")
+ private Object mHandlerLock = new Object();
+ @GuardedBy("mHandlerLock")
private ComponentName mUsbDeviceConnectionHandler;
public UsbHostManager(Context context, UsbAlsaManager alsaManager,
@@ -91,33 +80,33 @@ public class UsbHostManager {
}
public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
mCurrentSettings = settings;
}
}
private UsbProfileGroupSettingsManager getCurrentUserSettings() {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
return mCurrentSettings;
}
}
public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
- synchronized (mLock) {
+ synchronized (mHandlerLock) {
mUsbDeviceConnectionHandler = usbDeviceConnectionHandler;
}
}
private @Nullable ComponentName getUsbDeviceConnectionHandler() {
- synchronized (mLock) {
+ synchronized (mHandlerLock) {
return mUsbDeviceConnectionHandler;
}
}
- private boolean isBlackListed(String deviceName) {
+ private boolean isBlackListed(String deviceAddress) {
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
- if (deviceName.startsWith(mHostBlacklist[i])) {
+ if (deviceAddress.startsWith(mHostBlacklist[i])) {
return true;
}
}
@@ -136,166 +125,73 @@ public class UsbHostManager {
}
/* Called from JNI in monitorUsbHostBus() to report new USB devices
- Returns true if successful, in which case the JNI code will continue adding configurations,
- interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
- have been processed
+ Returns true if successful, i.e. the USB Audio device descriptors are
+ correctly parsed and the unique device is added to the audio device list.
*/
@SuppressWarnings("unused")
- private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
- int deviceClass, int deviceSubclass, int deviceProtocol,
- String manufacturerName, String productName, int version, String serialNumber) {
-
+ private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
+ byte[] descriptors) {
if (DEBUG) {
- Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
- // Audio Class Codes:
- // Audio: 0x01
- // Audio Subclass Codes:
- // undefined: 0x00
- // audio control: 0x01
- // audio streaming: 0x02
- // midi streaming: 0x03
-
- // some useful debugging info
- Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:"
- + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
+ Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
}
- // OK this is non-obvious, but true. One can't tell if the device being attached is even
- // potentially an audio device without parsing the interface descriptors, so punt on any
- // such test until endUsbDeviceAdded() when we have that info.
-
- if (isBlackListed(deviceName) ||
- isBlackListed(deviceClass, deviceSubclass)) {
+ // check class/subclass first as it is more likely to be blacklisted
+ if (isBlackListed(deviceClass, deviceSubclass) || isBlackListed(deviceAddress)) {
+ if (DEBUG) {
+ Slog.d(TAG, "device is black listed");
+ }
return false;
}
synchronized (mLock) {
- if (mDevices.get(deviceName) != null) {
- Slog.w(TAG, "device already on mDevices list: " + deviceName);
+ if (mDevices.get(deviceAddress) != null) {
+ Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
+ //TODO If this is the same peripheral as is being connected, replace
+ // it with the new connection.
return false;
}
- if (mNewDevice != null) {
- Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
- return false;
- }
-
- // Create version string in "%.%" format
- String versionString = Integer.toString(version >> 8) + "." + (version & 0xFF);
+ UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress);
+ if (parser.parseDescriptors(descriptors)) {
- mNewDevice = new UsbDevice(deviceName, vendorID, productID,
- deviceClass, deviceSubclass, deviceProtocol,
- manufacturerName, productName, versionString, serialNumber);
-
- mNewConfigurations = new ArrayList<>();
- mNewInterfaces = new ArrayList<>();
- mNewEndpoints = new ArrayList<>();
- }
-
- return true;
- }
-
- /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
- currently being added. Returns true if successful, false in case of error.
- */
- @SuppressWarnings("unused")
- private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
- if (mNewConfiguration != null) {
- mNewConfiguration.setInterfaces(
- mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
- mNewInterfaces.clear();
- }
-
- mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
- mNewConfigurations.add(mNewConfiguration);
- }
-
- /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
- currently being added. Returns true if successful, false in case of error.
- */
- @SuppressWarnings("unused")
- private void addUsbInterface(int id, String name, int altSetting,
- int Class, int subClass, int protocol) {
- if (mNewInterface != null) {
- mNewInterface.setEndpoints(
- mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
- mNewEndpoints.clear();
- }
-
- mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
- mNewInterfaces.add(mNewInterface);
- }
-
- /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
- currently being added. Returns true if successful, false in case of error.
- */
- @SuppressWarnings("unused")
- private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
- mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
- }
-
- /* Called from JNI in monitorUsbHostBus() to finish adding a new device */
- @SuppressWarnings("unused")
- private void endUsbDeviceAdded() {
- if (DEBUG) {
- Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
- }
- if (mNewInterface != null) {
- mNewInterface.setEndpoints(
- mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
- }
- if (mNewConfiguration != null) {
- mNewConfiguration.setInterfaces(
- mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
- }
-
-
- synchronized (mLock) {
- if (mNewDevice != null) {
- mNewDevice.setConfigurations(
- mNewConfigurations.toArray(
- new UsbConfiguration[mNewConfigurations.size()]));
- mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
- Slog.d(TAG, "Added device " + mNewDevice);
+ UsbDevice newDevice = parser.toAndroidUsbDevice();
+ mDevices.put(deviceAddress, newDevice);
// It is fine to call this only for the current user as all broadcasts are sent to
// all profiles of the user and the dialogs should only show once.
ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
if (usbDeviceConnectionHandler == null) {
- getCurrentUserSettings().deviceAttached(mNewDevice);
+ getCurrentUserSettings().deviceAttached(newDevice);
} else {
- getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
+ getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
usbDeviceConnectionHandler);
}
- // deviceName is something like: "/dev/bus/usb/001/001"
- UsbDescriptorParser parser = new UsbDescriptorParser();
- boolean isInputHeadset = false;
- boolean isOutputHeadset = false;
- if (parser.parseDevice(mNewDevice.getDeviceName())) {
- isInputHeadset = parser.isInputHeadset();
- isOutputHeadset = parser.isOutputHeadset();
- Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
- + " , out: " + isOutputHeadset + "]");
- }
- mUsbAlsaManager.usbDeviceAdded(mNewDevice,
- isInputHeadset, isOutputHeadset);
+
+ // Headset?
+ boolean isInputHeadset = parser.isInputHeadset();
+ boolean isOutputHeadset = parser.isOutputHeadset();
+ Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
+ + " , out: " + isOutputHeadset + "]");
+
+ mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
} else {
- Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
+ Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
+ return false;
}
- mNewDevice = null;
- mNewConfigurations = null;
- mNewInterfaces = null;
- mNewEndpoints = null;
- mNewConfiguration = null;
- mNewInterface = null;
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
+ }
+
+ return true;
}
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
- private void usbDeviceRemoved(String deviceName) {
+ private void usbDeviceRemoved(String deviceAddress) {
synchronized (mLock) {
- UsbDevice device = mDevices.remove(deviceName);
+ UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
mUsbAlsaManager.usbDeviceRemoved(device);
mSettingsManager.usbDeviceRemoved(device);
@@ -323,32 +219,35 @@ public class UsbHostManager {
}
/* Opens the specified USB device */
- public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings,
+ public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings,
String packageName, int uid) {
synchronized (mLock) {
- if (isBlackListed(deviceName)) {
+ if (isBlackListed(deviceAddress)) {
throw new SecurityException("USB device is on a restricted bus");
}
- UsbDevice device = mDevices.get(deviceName);
+ UsbDevice device = mDevices.get(deviceAddress);
if (device == null) {
// if it is not in mDevices, it either does not exist or is blacklisted
throw new IllegalArgumentException(
- "device " + deviceName + " does not exist or is restricted");
+ "device " + deviceAddress + " does not exist or is restricted");
}
+
settings.checkPermission(device, packageName, uid);
- return nativeOpenDevice(deviceName);
+ return nativeOpenDevice(deviceAddress);
}
}
public void dump(IndentingPrintWriter pw) {
+ pw.println("USB Host State:");
+ synchronized (mHandlerLock) {
+ if (mUsbDeviceConnectionHandler != null) {
+ pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
+ }
+ }
synchronized (mLock) {
- pw.println("USB Host State:");
for (String name : mDevices.keySet()) {
pw.println(" " + name + ": " + mDevices.get(name));
}
- if (mUsbDeviceConnectionHandler != null) {
- pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
- }
Collection<UsbDevice> devices = mDevices.values();
if (devices.size() != 0) {
@@ -356,17 +255,12 @@ public class UsbHostManager {
for (UsbDevice device : devices) {
StringBuilder stringBuilder = new StringBuilder();
- UsbDescriptorParser parser = new UsbDescriptorParser();
- if (parser.parseDevice(device.getDeviceName())) {
+ UsbDescriptorParser parser = new UsbDescriptorParser(device.getDeviceName());
+ if (parser.parseDevice()) {
UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
descriptorTree.parse(parser);
- UsbManager usbManager =
- (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
- UsbDeviceConnection connection = usbManager.openDevice(device);
-
- descriptorTree.report(new TextReportCanvas(connection, stringBuilder));
- connection.close();
+ descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
+ " , out: " + parser.isOutputHeadset() + "]");
@@ -382,5 +276,5 @@ public class UsbHostManager {
}
private native void monitorUsbHostBus();
- private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
+ private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ACHeader.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ACHeader.java
index a35b46318e23..7763150ad6b3 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb10ACHeader.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb10ACHeader.java
@@ -32,7 +32,7 @@ public final class Usb10ACHeader extends UsbACHeaderInterface {
// numbers associate with this endpoint
private byte mControls; // Vers 2.0 thing
- public Usb10ACHeader(int length, byte type, byte subtype, byte subclass, int spec) {
+ public Usb10ACHeader(int length, byte type, byte subtype, int subclass, int spec) {
super(length, type, subtype, subclass, spec);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ACInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ACInputTerminal.java
index 2363c4dd8009..75531d172761 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb10ACInputTerminal.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb10ACInputTerminal.java
@@ -32,7 +32,7 @@ public final class Usb10ACInputTerminal extends UsbACTerminal {
private byte mChannelNames; // 10:1 Unused (0x00)
private byte mTerminal; // 11:1 Unused (0x00)
- public Usb10ACInputTerminal(int length, byte type, byte subtype, byte subclass) {
+ public Usb10ACInputTerminal(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java
index d3486643ede2..c7634ba7dc63 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java
@@ -30,7 +30,7 @@ public final class Usb10ACMixerUnit extends UsbACMixerUnit {
private byte[] mControls; // bitmasks of which controls are present for each channel
private byte mNameID; // string descriptor ID of mixer name
- public Usb10ACMixerUnit(int length, byte type, byte subtype, byte subClass) {
+ public Usb10ACMixerUnit(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ACOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ACOutputTerminal.java
index 9f2f09ec146c..468ae57135cb 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb10ACOutputTerminal.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb10ACOutputTerminal.java
@@ -28,7 +28,7 @@ public final class Usb10ACOutputTerminal extends UsbACTerminal {
private byte mSourceID; // 7:1 From Input Terminal. (0x01)
private byte mTerminal; // 8:1 Unused.
- public Usb10ACOutputTerminal(int length, byte type, byte subtype, byte subClass) {
+ public Usb10ACOutputTerminal(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatI.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatI.java
index 1523bb528a03..1d8498ae33ff 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatI.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatI.java
@@ -33,7 +33,7 @@ public final class Usb10ASFormatI extends UsbASFormat {
// min & max rates otherwise mSamFreqType rates.
// All 3-byte values. All rates in Hz
- public Usb10ASFormatI(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ public Usb10ASFormatI(int length, byte type, byte subtype, byte formatType, int subclass) {
super(length, type, subtype, formatType, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatII.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatII.java
index b1e7680ee1b9..3c45790a8bfc 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatII.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatII.java
@@ -38,7 +38,7 @@ public final class Usb10ASFormatII extends UsbASFormat {
// the min & max rates. otherwise mSamFreqType rates.
// All 3-byte values. All rates in Hz
- public Usb10ASFormatII(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ public Usb10ASFormatII(int length, byte type, byte subtype, byte formatType, int subclass) {
super(length, type, subtype, formatType, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ASGeneral.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ASGeneral.java
index 2d4f604ed1a1..4fbbb2137e39 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb10ASGeneral.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb10ASGeneral.java
@@ -34,7 +34,7 @@ public final class Usb10ASGeneral extends UsbACInterface {
private int mFormatTag; // 5:2 The Audio Data Format that has to be used to communicate
// with this interface.
- public Usb10ASGeneral(int length, byte type, byte subtype, byte subclass) {
+ public Usb10ASGeneral(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java
index eefae3d51b3f..fe1b5023958c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java
@@ -29,7 +29,7 @@ public final class Usb20ACHeader extends UsbACHeaderInterface {
// See audio20.pdf Appendix A.7, “Audio Function Category Codes.”
private byte mControls; // 8:1 See audio20.pdf Table 4-5.
- public Usb20ACHeader(int length, byte type, byte subtype, byte subclass, int spec) {
+ public Usb20ACHeader(int length, byte type, byte subtype, int subclass, int spec) {
super(length, type, subtype, subclass, spec);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java
index 3e2ac39c0aca..ee1b32c1b544 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java
@@ -39,7 +39,7 @@ public final class Usb20ACInputTerminal extends UsbACTerminal {
private byte mTerminalName; // 16:1 - Index of a string descriptor, describing the
// Input Terminal.
- public Usb20ACInputTerminal(int length, byte type, byte subtype, byte subclass) {
+ public Usb20ACInputTerminal(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java
index 1b267a67752b..ab965856bb5d 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java
@@ -33,7 +33,7 @@ public final class Usb20ACMixerUnit extends UsbACMixerUnit {
private byte mNameID; // 12+p+N:1 Index of a string descriptor, describing the
// Mixer Unit.
- public Usb20ACMixerUnit(int length, byte type, byte subtype, byte subClass) {
+ public Usb20ACMixerUnit(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java
index 67478aad8a59..20a97af49bc5 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java
@@ -34,7 +34,7 @@ public final class Usb20ACOutputTerminal extends UsbACTerminal {
private int mControls; // 9:2 - see Audio20.pdf Table 4-10
private byte mTerminalID; // 11:1 - Index of a string descriptor, describing the
- public Usb20ACOutputTerminal(int length, byte type, byte subtype, byte subClass) {
+ public Usb20ACOutputTerminal(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java
index c03199619e74..7310a3e2c5ab 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java
@@ -31,7 +31,7 @@ public final class Usb20ASFormatI extends UsbASFormat {
private byte mBitResolution; // 5:1 The number of effectively used bits from
// the available bits in an audio subslot.
- public Usb20ASFormatI(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ public Usb20ASFormatI(int length, byte type, byte subtype, byte formatType, int subclass) {
super(length, type, subtype, formatType, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java
index dc44ff063964..fe8874328e77 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java
@@ -34,7 +34,7 @@ public final class Usb20ASFormatII extends UsbASFormat {
/**
* TBD
*/
- public Usb20ASFormatII(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ public Usb20ASFormatII(int length, byte type, byte subtype, byte formatType, int subclass) {
super(length, type, subtype, formatType, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java
index b44a216703f8..b0ba02ff22d3 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java
@@ -31,7 +31,7 @@ public final class Usb20ASFormatIII extends UsbASFormat {
private byte mBitResolution; // 5:1 The number of effectively used bits from
// the available bits in an audio subframe.
- public Usb20ASFormatIII(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ public Usb20ASFormatIII(int length, byte type, byte subtype, byte formatType, int subclass) {
super(length, type, subtype, formatType, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java
index 18d48a009098..de2073882e3a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java
+++ b/services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java
@@ -41,7 +41,7 @@ public final class Usb20ASGeneral extends UsbACInterface {
private byte mChannelNames; // 15:1 Index of a string descriptor, describing the
// name of the first physical channel.
- public Usb20ASGeneral(int length, byte type, byte subtype, byte subclass) {
+ public Usb20ASGeneral(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
index 6e1ce07536c5..409e605c3c2f 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
@@ -38,7 +38,7 @@ public class UsbACAudioControlEndpoint extends UsbACEndpoint {
static final byte ATTRIBSMASK_SYNC = 0x0C;
static final byte ATTRIBMASK_TRANS = 0x03;
- public UsbACAudioControlEndpoint(int length, byte type, byte subclass) {
+ public UsbACAudioControlEndpoint(int length, byte type, int subclass) {
super(length, type, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
index d35190298df6..e63bb74abdf7 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
@@ -24,7 +24,7 @@ public class UsbACAudioStreamEndpoint extends UsbACEndpoint {
private static final String TAG = "UsbACAudioStreamEndpoint";
//TODO data fields...
- public UsbACAudioStreamEndpoint(int length, byte type, byte subclass) {
+ public UsbACAudioStreamEndpoint(int length, byte type, int subclass) {
super(length, type, subclass);
}
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 4a6839d943ff..7ebccf39868c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -25,16 +25,16 @@ import android.util.Log;
abstract class UsbACEndpoint extends UsbDescriptor {
private static final String TAG = "UsbACEndpoint";
- protected final byte mSubclass; // from the mSubclass member of the "enclosing"
- // Interface Descriptor, not the stream.
- protected byte mSubtype; // 2:1 HEADER descriptor subtype
+ protected final int mSubclass; // from the mSubclass member of the "enclosing"
+ // Interface Descriptor, not the stream.
+ protected byte mSubtype; // 2:1 HEADER descriptor subtype
- UsbACEndpoint(int length, byte type, byte subclass) {
+ UsbACEndpoint(int length, byte type, int subclass) {
super(length, type);
mSubclass = subclass;
}
- public byte getSubclass() {
+ public int getSubclass() {
return mSubclass;
}
@@ -52,7 +52,7 @@ abstract class UsbACEndpoint extends UsbDescriptor {
public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
int length, byte type) {
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
- byte subClass = interfaceDesc.getUsbSubclass();
+ int subClass = interfaceDesc.getUsbSubclass();
switch (subClass) {
case AUDIO_AUDIOCONTROL:
return new UsbACAudioControlEndpoint(length, type, subClass);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
index ab3903b402d9..2c7ef7988385 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
@@ -46,7 +46,7 @@ public final class UsbACFeatureUnit extends UsbACInterface {
// logical channel
private byte mUnitName; // ?:1 Index of a string descriptor, describing this Feature Unit.
- public UsbACFeatureUnit(int length, byte type, byte subtype, byte subClass) {
+ public UsbACFeatureUnit(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java
index 01a355e2c6e4..88d026eb02a3 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java
@@ -31,7 +31,7 @@ public abstract class UsbACHeaderInterface extends UsbACInterface {
// of this descriptor header and all Unit and Terminal descriptors.
public UsbACHeaderInterface(
- int length, byte type, byte subtype, byte subclass, int adcRelease) {
+ int length, byte type, byte subtype, int subclass, int adcRelease) {
super(length, type, subtype, subclass);
mADCRelease = adcRelease;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
index df6c53fa9f52..38c12a1f6c16 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
@@ -78,10 +78,10 @@ public abstract class UsbACInterface extends UsbDescriptor {
public static final int FORMAT_III_IEC1937_MPEG2_Layer1LS = 0x2005;
protected final byte mSubtype; // 2:1 HEADER descriptor subtype
- protected final byte mSubclass; // from the mSubclass member of the
+ protected final int mSubclass; // from the mSubclass member of the
// "enclosing" Interface Descriptor
- public UsbACInterface(int length, byte type, byte subtype, byte subclass) {
+ public UsbACInterface(int length, byte type, byte subtype, int subclass) {
super(length, type);
mSubtype = subtype;
mSubclass = subclass;
@@ -91,12 +91,12 @@ public abstract class UsbACInterface extends UsbDescriptor {
return mSubtype;
}
- public byte getSubclass() {
+ public int getSubclass() {
return mSubclass;
}
private static UsbDescriptor allocAudioControlDescriptor(UsbDescriptorParser parser,
- ByteStream stream, int length, byte type, byte subtype, byte subClass) {
+ ByteStream stream, int length, byte type, byte subtype, int subClass) {
switch (subtype) {
case ACI_HEADER:
{
@@ -157,7 +157,7 @@ public abstract class UsbACInterface extends UsbDescriptor {
}
private static UsbDescriptor allocAudioStreamingDescriptor(UsbDescriptorParser parser,
- ByteStream stream, int length, byte type, byte subtype, byte subClass) {
+ ByteStream stream, int length, byte type, byte subtype, int subClass) {
//int spec = parser.getUsbSpec();
int acInterfaceSpec = parser.getACInterfaceSpec();
switch (subtype) {
@@ -182,7 +182,7 @@ public abstract class UsbACInterface extends UsbDescriptor {
}
private static UsbDescriptor allocMidiStreamingDescriptor(int length, byte type,
- byte subtype, byte subClass) {
+ byte subtype, int subClass) {
switch (subtype) {
case MSI_HEADER:
return new UsbMSMidiHeader(length, type, subtype, subClass);
@@ -212,7 +212,7 @@ public abstract class UsbACInterface extends UsbDescriptor {
int length, byte type) {
byte subtype = stream.getByte();
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
- byte subClass = interfaceDesc.getUsbSubclass();
+ int subClass = interfaceDesc.getUsbSubclass();
switch (subClass) {
case AUDIO_AUDIOCONTROL:
return allocAudioControlDescriptor(
@@ -236,7 +236,7 @@ public abstract class UsbACInterface extends UsbDescriptor {
public void report(ReportCanvas canvas) {
super.report(canvas);
- byte subClass = getSubclass();
+ int subClass = getSubclass();
String subClassName = UsbStrings.getACInterfaceSubclassName(subClass);
byte subtype = getSubtype();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java
index 9e00a7976dfd..bd027aebff45 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java
@@ -22,7 +22,7 @@ package com.android.server.usb.descriptors;
public final class UsbACInterfaceUnparsed extends UsbACInterface {
private static final String TAG = "UsbACInterfaceUnparsed";
- public UsbACInterfaceUnparsed(int length, byte type, byte subtype, byte subClass) {
+ public UsbACInterfaceUnparsed(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
index 9c314575ccc4..42ee88922edd 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
@@ -28,7 +28,7 @@ public final class UsbACMidiEndpoint extends UsbACEndpoint {
private byte mNumJacks;
private byte[] mJackIds;
- public UsbACMidiEndpoint(int length, byte type, byte subclass) {
+ public UsbACMidiEndpoint(int length, byte type, int subclass) {
super(length, type, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
index 88faed962a54..606fa2b4c7f8 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
@@ -24,7 +24,7 @@ public class UsbACMixerUnit extends UsbACInterface {
// are connected.
protected byte mNumOutputs; // The number of output channels
- public UsbACMixerUnit(int length, byte type, byte subtype, byte subClass) {
+ public UsbACMixerUnit(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
index b16bc575e806..4644fe14969f 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
@@ -32,7 +32,7 @@ public final class UsbACSelectorUnit extends UsbACInterface {
// Input Pin of this Selector Unit is connected.
private byte mNameIndex; // Index of a string descriptor, describing the Selector Unit.
- public UsbACSelectorUnit(int length, byte type, byte subtype, byte subClass) {
+ public UsbACSelectorUnit(int length, byte type, byte subtype, int subClass) {
super(length, type, subtype, subClass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
index 2836508581d8..36139d6c6900 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
@@ -32,7 +32,7 @@ public abstract class UsbACTerminal extends UsbACInterface {
protected int mTerminalType; // 4:2 USB Streaming. (0x0101)
protected byte mAssocTerminal; // 6:1 Unused (0x00)
- public UsbACTerminal(int length, byte type, byte subtype, byte subclass) {
+ public UsbACTerminal(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java b/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
index 7a92f9e197ec..5e515a147f33 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
@@ -40,7 +40,7 @@ public class UsbASFormat extends UsbACInterface {
public static final byte EXT_FORMAT_TYPE_II = (byte) 0x82;
public static final byte EXT_FORMAT_TYPE_III = (byte) 0x83;
- public UsbASFormat(int length, byte type, byte subtype, byte formatType, byte mSubclass) {
+ public UsbASFormat(int length, byte type, byte subtype, byte formatType, int mSubclass) {
super(length, type, subtype, mSubclass);
mFormatType = formatType;
}
@@ -66,8 +66,8 @@ public class UsbASFormat extends UsbACInterface {
* stream.
*/
public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
- ByteStream stream, int length, byte type,
- byte subtype, byte subclass) {
+ ByteStream stream, int length, byte type,
+ byte subtype, int subclass) {
byte formatType = stream.getByte();
int acInterfaceSpec = parser.getACInterfaceSpec();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 75279c61c4f0..993778fa3991 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -15,8 +15,13 @@
*/
package com.android.server.usb.descriptors;
+import android.hardware.usb.UsbConfiguration;
+import android.hardware.usb.UsbInterface;
+
import com.android.server.usb.descriptors.report.ReportCanvas;
+import java.util.ArrayList;
+
/**
* @hide
* An USB Config Descriptor.
@@ -25,15 +30,18 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
public final class UsbConfigDescriptor extends UsbDescriptor {
private static final String TAG = "UsbConfigDescriptor";
- private int mTotalLength; // 2:2 Total length in bytes of data returned
+ private int mTotalLength; // 2:2 Total length in bytes of data returned
private byte mNumInterfaces; // 4:1 Number of Interfaces
- private byte mConfigValue; // 5:1 Value to use as an argument to select this configuration
- private byte mConfigIndex; // 6:1 Index of String Descriptor describing this configuration
- private byte mAttribs; // 7:1 D7 Reserved, set to 1. (USB 1.0 Bus Powered)
- // D6 Self Powered
- // D5 Remote Wakeup
- // D4..0 Reserved, set to 0.
- private byte mMaxPower; // 8:1 Maximum Power Consumption in 2mA units
+ private int mConfigValue; // 5:1 Value to use as an argument to select this configuration
+ private byte mConfigIndex; // 6:1 Index of String Descriptor describing this configuration
+ private int mAttribs; // 7:1 D7 Reserved, set to 1. (USB 1.0 Bus Powered)
+ // D6 Self Powered
+ // D5 Remote Wakeup
+ // D4..0 Reserved, set to 0.
+ private int mMaxPower; // 8:1 Maximum Power Consumption in 2mA units
+
+ private ArrayList<UsbInterfaceDescriptor> mInterfaceDescriptors =
+ new ArrayList<UsbInterfaceDescriptor>();
UsbConfigDescriptor(int length, byte type) {
super(length, type);
@@ -48,7 +56,7 @@ public final class UsbConfigDescriptor extends UsbDescriptor {
return mNumInterfaces;
}
- public byte getConfigValue() {
+ public int getConfigValue() {
return mConfigValue;
}
@@ -56,22 +64,38 @@ public final class UsbConfigDescriptor extends UsbDescriptor {
return mConfigIndex;
}
- public byte getAttribs() {
+ public int getAttribs() {
return mAttribs;
}
- public byte getMaxPower() {
+ public int getMaxPower() {
return mMaxPower;
}
+ void addInterfaceDescriptor(UsbInterfaceDescriptor interfaceDesc) {
+ mInterfaceDescriptors.add(interfaceDesc);
+ }
+
+ UsbConfiguration toAndroid(UsbDescriptorParser parser) {
+ String name = parser.getDescriptorString(mConfigIndex);
+ UsbConfiguration config = new
+ UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
+ UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
+ for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
+ interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser);
+ }
+ config.setInterfaces(interfaces);
+ return config;
+ }
+
@Override
public int parseRawDescriptors(ByteStream stream) {
mTotalLength = stream.unpackUsbShort();
mNumInterfaces = stream.getByte();
- mConfigValue = stream.getByte();
+ mConfigValue = stream.getUnsignedByte();
mConfigIndex = stream.getByte();
- mAttribs = stream.getByte();
- mMaxPower = stream.getByte();
+ mAttribs = stream.getUnsignedByte();
+ mMaxPower = stream.getUnsignedByte();
return mLength;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
index 8c7565b790d2..3fc5fe320574 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -85,36 +85,36 @@ public abstract class UsbDescriptor implements Reporting {
public static final byte DESCRIPTORTYPE_ENDPOINT_COMPANION = 0x30; // 48
// Class IDs
- public static final byte CLASSID_DEVICE = 0x00;
- public static final byte CLASSID_AUDIO = 0x01;
- public static final byte CLASSID_COM = 0x02;
- public static final byte CLASSID_HID = 0x03;
- // public static final byte CLASSID_??? = 0x04;
- public static final byte CLASSID_PHYSICAL = 0x05;
- public static final byte CLASSID_IMAGE = 0x06;
- public static final byte CLASSID_PRINTER = 0x07;
- public static final byte CLASSID_STORAGE = 0x08;
- public static final byte CLASSID_HUB = 0x09;
- public static final byte CLASSID_CDC_CONTROL = 0x0A;
- public static final byte CLASSID_SMART_CARD = 0x0B;
- //public static final byte CLASSID_??? = 0x0C;
- public static final byte CLASSID_SECURITY = 0x0D;
- public static final byte CLASSID_VIDEO = 0x0E;
- public static final byte CLASSID_HEALTHCARE = 0x0F;
- public static final byte CLASSID_AUDIOVIDEO = 0x10;
- public static final byte CLASSID_BILLBOARD = 0x11;
- public static final byte CLASSID_TYPECBRIDGE = 0x12;
- public static final byte CLASSID_DIAGNOSTIC = (byte) 0xDC;
- public static final byte CLASSID_WIRELESS = (byte) 0xE0;
- public static final byte CLASSID_MISC = (byte) 0xEF;
- public static final byte CLASSID_APPSPECIFIC = (byte) 0xFE;
- public static final byte CLASSID_VENDSPECIFIC = (byte) 0xFF;
+ public static final int CLASSID_DEVICE = 0x00;
+ public static final int CLASSID_AUDIO = 0x01;
+ public static final int CLASSID_COM = 0x02;
+ public static final int CLASSID_HID = 0x03;
+ // public static final int CLASSID_??? = 0x04;
+ public static final int CLASSID_PHYSICAL = 0x05;
+ public static final int CLASSID_IMAGE = 0x06;
+ public static final int CLASSID_PRINTER = 0x07;
+ public static final int CLASSID_STORAGE = 0x08;
+ public static final int CLASSID_HUB = 0x09;
+ public static final int CLASSID_CDC_CONTROL = 0x0A;
+ public static final int CLASSID_SMART_CARD = 0x0B;
+ //public static final int CLASSID_??? = 0x0C;
+ public static final int CLASSID_SECURITY = 0x0D;
+ public static final int CLASSID_VIDEO = 0x0E;
+ public static final int CLASSID_HEALTHCARE = 0x0F;
+ public static final int CLASSID_AUDIOVIDEO = 0x10;
+ public static final int CLASSID_BILLBOARD = 0x11;
+ public static final int CLASSID_TYPECBRIDGE = 0x12;
+ public static final int CLASSID_DIAGNOSTIC = 0xDC;
+ public static final int CLASSID_WIRELESS = 0xE0;
+ public static final int CLASSID_MISC = 0xEF;
+ public static final int CLASSID_APPSPECIFIC = 0xFE;
+ public static final int CLASSID_VENDSPECIFIC = 0xFF;
// Audio Subclass codes
- public static final byte AUDIO_SUBCLASS_UNDEFINED = 0x00;
- public static final byte AUDIO_AUDIOCONTROL = 0x01;
- public static final byte AUDIO_AUDIOSTREAMING = 0x02;
- public static final byte AUDIO_MIDISTREAMING = 0x03;
+ public static final int AUDIO_SUBCLASS_UNDEFINED = 0x00;
+ public static final int AUDIO_AUDIOCONTROL = 0x01;
+ public static final int AUDIO_AUDIOSTREAMING = 0x02;
+ public static final int AUDIO_MIDISTREAMING = 0x03;
// Request IDs
public static final int REQUEST_GET_STATUS = 0x00;
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 ad7bde5c275e..6c6bd01316af 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -15,6 +15,7 @@
*/
package com.android.server.usb.descriptors;
+import android.hardware.usb.UsbDevice;
import android.util.Log;
import java.util.ArrayList;
@@ -25,11 +26,16 @@ import java.util.ArrayList;
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
+ private static final boolean DEBUG = false;
+
+ private final String mDeviceAddr;
// Descriptor Objects
+ private static final int DESCRIPTORS_ALLOC_SIZE = 128;
private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
private UsbDeviceDescriptor mDeviceDescriptor;
+ private UsbConfigDescriptor mCurConfigDescriptor;
private UsbInterfaceDescriptor mCurInterfaceDescriptor;
// The AudioClass spec implemented by the AudioClass Interfaces
@@ -37,7 +43,13 @@ public final class UsbDescriptorParser {
// Obtained from the first AudioClass Header descriptor.
private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
- public UsbDescriptorParser() {}
+ public UsbDescriptorParser(String deviceAddr) {
+ mDeviceAddr = deviceAddr;
+ }
+
+ public String getDeviceAddr() {
+ return mDeviceAddr;
+ }
/**
* @return the USB Spec value associated with the Device descriptor for the
@@ -60,6 +72,18 @@ public final class UsbDescriptorParser {
public int getACInterfaceSpec() {
return mACInterfacesSpec;
}
+
+ private class UsbDescriptorsStreamFormatException extends Exception {
+ String mMessage;
+ UsbDescriptorsStreamFormatException(String message) {
+ mMessage = message;
+ }
+
+ public String toString() {
+ return "Descriptor Stream Format Exception: " + mMessage;
+ }
+ }
+
/**
* The probability (as returned by getHeadsetProbability() at which we conclude
* the peripheral is a headset.
@@ -67,7 +91,8 @@ public final class UsbDescriptorParser {
private static final float IN_HEADSET_TRIGGER = 0.75f;
private static final float OUT_HEADSET_TRIGGER = 0.75f;
- private UsbDescriptor allocDescriptor(ByteStream stream) {
+ private UsbDescriptor allocDescriptor(ByteStream stream)
+ throws UsbDescriptorsStreamFormatException {
stream.resetReadCount();
int length = stream.getUnsignedByte();
@@ -83,15 +108,38 @@ public final class UsbDescriptorParser {
break;
case UsbDescriptor.DESCRIPTORTYPE_CONFIG:
- descriptor = new UsbConfigDescriptor(length, type);
+ descriptor = mCurConfigDescriptor = new UsbConfigDescriptor(length, type);
+ if (mDeviceDescriptor != null) {
+ mDeviceDescriptor.addConfigDescriptor(mCurConfigDescriptor);
+ } else {
+ Log.e(TAG, "Config Descriptor found with no associated Device Descriptor!");
+ throw new UsbDescriptorsStreamFormatException(
+ "Config Descriptor found with no associated Device Descriptor!");
+ }
break;
case UsbDescriptor.DESCRIPTORTYPE_INTERFACE:
descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type);
+ if (mCurConfigDescriptor != null) {
+ mCurConfigDescriptor.addInterfaceDescriptor(mCurInterfaceDescriptor);
+ } else {
+ Log.e(TAG, "Interface Descriptor found with no associated Config Descriptor!");
+ throw new UsbDescriptorsStreamFormatException(
+ "Interface Descriptor found with no associated Config Descriptor!");
+ }
break;
case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
descriptor = new UsbEndpointDescriptor(length, type);
+ if (mCurInterfaceDescriptor != null) {
+ mCurInterfaceDescriptor.addEndpointDescriptor(
+ (UsbEndpointDescriptor) descriptor);
+ } else {
+ Log.e(TAG,
+ "Endpoint Descriptor found with no associated Interface Descriptor!");
+ throw new UsbDescriptorsStreamFormatException(
+ "Endpoint Descriptor found with no associated Interface Descriptor!");
+ }
break;
/*
@@ -144,8 +192,12 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
- public void parseDescriptors(byte[] descriptors) {
- mDescriptors.clear();
+ public boolean parseDescriptors(byte[] descriptors) {
+ if (DEBUG) {
+ Log.d(TAG, "parseDescriptors() - start");
+ }
+ // This will allow us to (probably) alloc mDescriptors just once.
+ mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
ByteStream stream = new ByteStream(descriptors);
while (stream.available() > 0) {
@@ -173,21 +225,36 @@ public final class UsbDescriptorParser {
}
}
}
+ if (DEBUG) {
+ Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors.");
+ }
+ return true;
}
/**
* @hide
*/
- public boolean parseDevice(String deviceAddr) {
- byte[] rawDescriptors = getRawDescriptors(deviceAddr);
- if (rawDescriptors != null) {
- parseDescriptors(rawDescriptors);
- return true;
- }
- return false;
+ public boolean parseDevice() {
+ byte[] rawDescriptors = getRawDescriptors();
+
+ return rawDescriptors != null
+ ? parseDescriptors(rawDescriptors) : false;
+ }
+
+ private byte[] getRawDescriptors() {
+ return getRawDescriptors_native(mDeviceAddr);
+ }
+
+ private native byte[] getRawDescriptors_native(String deviceAddr);
+
+ /**
+ * @hide
+ */
+ public String getDescriptorString(int stringId) {
+ return getDescriptorString_native(mDeviceAddr, stringId);
}
- private native byte[] getRawDescriptors(String deviceAddr);
+ private native String getDescriptorString_native(String deviceAddr, int stringId);
public int getParsingSpec() {
return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0;
@@ -200,6 +267,17 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
+ public UsbDevice toAndroidUsbDevice() {
+ if (mDeviceDescriptor == null) {
+ return null;
+ }
+
+ return mDeviceDescriptor.toAndroid(this);
+ }
+
+ /**
+ * @hide
+ */
public ArrayList<UsbDescriptor> getDescriptors(byte type) {
ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
for (UsbDescriptor descriptor : mDescriptors) {
@@ -213,7 +291,7 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
- public ArrayList<UsbDescriptor> getInterfaceDescriptorsForClass(byte usbClass) {
+ public ArrayList<UsbDescriptor> getInterfaceDescriptorsForClass(int usbClass) {
ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
for (UsbDescriptor descriptor : mDescriptors) {
// ensure that this isn't an unrecognized DESCRIPTORTYPE_INTERFACE
@@ -235,7 +313,7 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
- public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, byte subclass) {
+ public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, int subclass) {
ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
for (UsbDescriptor descriptor : mDescriptors) {
if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE) {
@@ -355,8 +433,6 @@ public final class UsbDescriptorParser {
* to count on the peripheral being a headset.
*/
public boolean isInputHeadset() {
- // TEMP
- Log.i(TAG, "---- isInputHeadset() prob:" + (getInputHeadsetProbability() * 100f) + "%");
return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
}
@@ -410,8 +486,6 @@ public final class UsbDescriptorParser {
* to count on the peripheral being a headset.
*/
public boolean isOutputHeadset() {
- // TEMP
- Log.i(TAG, "---- isOutputHeadset() prob:" + (getOutputHeadsetProbability() * 100f) + "%");
return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
}
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 d5cb89ea82e6..8e7f0fde1537 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -15,9 +15,14 @@
*/
package com.android.server.usb.descriptors;
+import android.hardware.usb.UsbConfiguration;
+import android.hardware.usb.UsbDevice;
+
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
+import java.util.ArrayList;
+
/**
* @hide
* A USB Device Descriptor.
@@ -31,9 +36,9 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
public static final int USBSPEC_2_0 = 0x0200;
private int mSpec; // 2:2 bcdUSB 2 BCD USB Specification Number - BCD
- private byte mDevClass; // 4:1 class code
- private byte mDevSubClass; // 5:1 subclass code
- private byte mProtocol; // 6:1 protocol
+ private int mDevClass; // 4:1 class code
+ private int mDevSubClass; // 5:1 subclass code
+ private int mProtocol; // 6:1 protocol
private byte mPacketSize; // 7:1 Maximum Packet Size for Zero Endpoint.
// Valid Sizes are 8, 16, 32, 64
private int mVendorID; // 8:2 vendor ID
@@ -44,6 +49,9 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
private byte mSerialNum; // 16:1 Index of Serial Number String Descriptor
private byte mNumConfigs; // 17:1 Number of Possible Configurations
+ private ArrayList<UsbConfigDescriptor> mConfigDescriptors =
+ new ArrayList<UsbConfigDescriptor>();
+
UsbDeviceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 1;
@@ -53,15 +61,15 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
return mSpec;
}
- public byte getDevClass() {
+ public int getDevClass() {
return mDevClass;
}
- public byte getDevSubClass() {
+ public int getDevSubClass() {
return mDevSubClass;
}
- public byte getProtocol() {
+ public int getProtocol() {
return mProtocol;
}
@@ -97,12 +105,41 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
return mNumConfigs;
}
+ void addConfigDescriptor(UsbConfigDescriptor config) {
+ mConfigDescriptors.add(config);
+ }
+
+ /**
+ * @hide
+ */
+ public UsbDevice toAndroid(UsbDescriptorParser parser) {
+ String mfgName = parser.getDescriptorString(mMfgIndex);
+ String prodName = parser.getDescriptorString(mProductIndex);
+
+ // Create version string in "%.%" format
+ String versionString =
+ Integer.toString(mDeviceRelease >> 8) + "." + (mDeviceRelease & 0xFF);
+ String serialStr = parser.getDescriptorString(mSerialNum);
+
+ UsbDevice device = new UsbDevice(parser.getDeviceAddr(), mVendorID, mProductID,
+ mDevClass, mDevSubClass,
+ mProtocol, mfgName, prodName,
+ versionString, serialStr);
+ UsbConfiguration[] configs = new UsbConfiguration[mConfigDescriptors.size()];
+ for (int index = 0; index < mConfigDescriptors.size(); index++) {
+ configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
+ }
+ device.setConfigurations(configs);
+
+ return device;
+ }
+
@Override
public int parseRawDescriptors(ByteStream stream) {
mSpec = stream.unpackUsbShort();
- mDevClass = stream.getByte();
- mDevSubClass = stream.getByte();
- mProtocol = stream.getByte();
+ mDevClass = stream.getUnsignedByte();
+ mDevSubClass = stream.getUnsignedByte();
+ mProtocol = stream.getUnsignedByte();
mPacketSize = stream.getByte();
mVendorID = stream.unpackUsbShort();
mProductID = stream.unpackUsbShort();
@@ -124,9 +161,9 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
int spec = getSpec();
canvas.writeListItem("Spec: " + ReportCanvas.getBCDString(spec));
- byte devClass = getDevClass();
+ int devClass = getDevClass();
String classStr = UsbStrings.getClassName(devClass);
- byte devSubClass = getDevSubClass();
+ int devSubClass = getDevSubClass();
String subClasStr = UsbStrings.getClassName(devSubClass);
canvas.writeListItem("Class " + devClass + ": " + classStr + " Subclass"
+ devSubClass + ": " + subClasStr);
@@ -134,12 +171,11 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
+ " Product ID: " + ReportCanvas.getHexString(getProductID())
+ " Product Release: " + ReportCanvas.getBCDString(getDeviceRelease()));
+ UsbDescriptorParser parser = canvas.getParser();
byte mfgIndex = getMfgIndex();
- String manufacturer =
- UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), mfgIndex);
+ String manufacturer = parser.getDescriptorString(mfgIndex);
byte productIndex = getProductIndex();
- String product =
- UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), productIndex);
+ String product = parser.getDescriptorString(productIndex);
canvas.writeListItem("Manufacturer " + mfgIndex + ": " + manufacturer
+ " Product " + productIndex + ": " + product);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 6322fbe8b45b..11302380b589 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -15,6 +15,8 @@
*/
package com.android.server.usb.descriptors;
+import android.hardware.usb.UsbEndpoint;
+
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
@@ -25,16 +27,16 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
public class UsbEndpointDescriptor extends UsbDescriptor {
private static final String TAG = "UsbEndpointDescriptor";
- public static final byte MASK_ENDPOINT_ADDRESS = 0b0001111;
- public static final byte MASK_ENDPOINT_DIRECTION = (byte) 0b10000000;
- public static final byte DIRECTION_OUTPUT = 0x00;
- public static final byte DIRECTION_INPUT = (byte) 0x80;
+ public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
+ public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
+ public static final int DIRECTION_OUTPUT = 0x0000;
+ public static final int DIRECTION_INPUT = (byte) 0x0080;
- public static final byte MASK_ATTRIBS_TRANSTYPE = 0b00000011;
- public static final byte TRANSTYPE_CONTROL = 0x00;
- public static final byte TRANSTYPE_ISO = 0x01;
- public static final byte TRANSTYPE_BULK = 0x02;
- public static final byte TRANSTYPE_INTERRUPT = 0x03;
+ public static final int MASK_ATTRIBS_TRANSTYPE = 0b00000011;
+ public static final int TRANSTYPE_CONTROL = 0x00;
+ public static final int TRANSTYPE_ISO = 0x01;
+ public static final int TRANSTYPE_BULK = 0x02;
+ public static final int TRANSTYPE_INTERRUPT = 0x03;
public static final byte MASK_ATTRIBS_SYNCTYPE = 0b00001100;
public static final byte SYNCTYPE_NONE = 0b00000000;
@@ -42,18 +44,18 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
public static final byte SYNCTYPE_ADAPTSYNC = 0b00001000;
public static final byte SYNCTYPE_RESERVED = 0b00001100;
- public static final byte MASK_ATTRIBS_USEAGE = 0b00110000;
- public static final byte USEAGE_DATA = 0b00000000;
- public static final byte USEAGE_FEEDBACK = 0b00010000;
- public static final byte USEAGE_EXPLICIT = 0b00100000;
- public static final byte USEAGE_RESERVED = 0b00110000;
+ public static final int MASK_ATTRIBS_USEAGE = 0b00110000;
+ public static final int USEAGE_DATA = 0b00000000;
+ public static final int USEAGE_FEEDBACK = 0b00010000;
+ public static final int USEAGE_EXPLICIT = 0b00100000;
+ public static final int USEAGE_RESERVED = 0b00110000;
- private byte mEndpointAddress; // 2:1 Endpoint Address
+ private int mEndpointAddress; // 2:1 Endpoint Address
// Bits 0..3b Endpoint Number.
// Bits 4..6b Reserved. Set to Zero
// Bits 7 Direction 0 = Out, 1 = In
// (Ignored for Control Endpoints)
- private byte mAttributes; // 3:1 Various flags
+ private int mAttributes; // 3:1 Various flags
// Bits 0..1 Transfer Type:
// 00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt
// Bits 2..7 are reserved. If Isochronous endpoint,
@@ -69,7 +71,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
// 11: Reserved
private int mPacketSize; // 4:2 Maximum Packet Size this endpoint is capable of
// sending or receiving
- private byte mInterval; // 6:1 Interval for polling endpoint data transfers. Value in
+ private int mInterval; // 6:1 Interval for polling endpoint data transfers. Value in
// frame counts.
// Ignored for Bulk & Control Endpoints. Isochronous must equal
// 1 and field may range from 1 to 255 for interrupt endpoints.
@@ -81,11 +83,11 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
mHierarchyLevel = 4;
}
- public byte getEndpointAddress() {
+ public int getEndpointAddress() {
return mEndpointAddress;
}
- public byte getAttributes() {
+ public int getAttributes() {
return mAttributes;
}
@@ -93,7 +95,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
return mPacketSize;
}
- public byte getInterval() {
+ public int getInterval() {
return mInterval;
}
@@ -105,12 +107,16 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
return mSyncAddress;
}
+ /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
+ return new UsbEndpoint(mEndpointAddress, mAttributes, mPacketSize, mInterval);
+ }
+
@Override
public int parseRawDescriptors(ByteStream stream) {
- mEndpointAddress = stream.getByte();
- mAttributes = stream.getByte();
+ mEndpointAddress = stream.getUnsignedByte();
+ mAttributes = stream.getUnsignedByte();
mPacketSize = stream.unpackUsbShort();
- mInterval = stream.getByte();
+ mInterval = stream.getUnsignedByte();
if (mLength == 9) {
mRefresh = stream.getByte();
mSyncAddress = stream.getByte();
@@ -124,13 +130,13 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
canvas.openList();
- byte address = getEndpointAddress();
+ int address = getEndpointAddress();
canvas.writeListItem("Address: "
+ ReportCanvas.getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
+ ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
== UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
- byte attributes = getAttributes();
+ int attributes = getAttributes();
canvas.openListItem();
canvas.write("Attributes: " + ReportCanvas.getHexString(attributes) + " ");
switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 4eef6caf5a60..d87b1afba79a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -15,9 +15,14 @@
*/
package com.android.server.usb.descriptors;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
+import java.util.ArrayList;
+
/**
* @hide
* A common super-class for all USB Interface Descritor subtypes.
@@ -26,14 +31,17 @@ import com.android.server.usb.descriptors.report.UsbStrings;
public class UsbInterfaceDescriptor extends UsbDescriptor {
private static final String TAG = "UsbInterfaceDescriptor";
- protected byte mInterfaceNumber; // 2:1 Number of Interface
+ protected int mInterfaceNumber; // 2:1 Number of Interface
protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
protected byte mNumEndpoints; // 4:1 Number of Endpoints used for this interface
- protected byte mUsbClass; // 5:1 Class Code
- protected byte mUsbSubclass; // 6:1 Subclass Code
- protected byte mProtocol; // 7:1 Protocol Code
+ protected int mUsbClass; // 5:1 Class Code
+ protected int mUsbSubclass; // 6:1 Subclass Code
+ protected int mProtocol; // 7:1 Protocol Code
protected byte mDescrIndex; // 8:1 Index of String Descriptor Describing this interface
+ private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
+ new ArrayList<UsbEndpointDescriptor>();
+
UsbInterfaceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 3;
@@ -41,18 +49,18 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
@Override
public int parseRawDescriptors(ByteStream stream) {
- mInterfaceNumber = stream.getByte();
+ mInterfaceNumber = stream.getUnsignedByte();
mAlternateSetting = stream.getByte();
mNumEndpoints = stream.getByte();
- mUsbClass = stream.getByte();
- mUsbSubclass = stream.getByte();
- mProtocol = stream.getByte();
+ mUsbClass = stream.getUnsignedByte();
+ mUsbSubclass = stream.getUnsignedByte();
+ mProtocol = stream.getUnsignedByte();
mDescrIndex = stream.getByte();
return mLength;
}
- public byte getInterfaceNumber() {
+ public int getInterfaceNumber() {
return mInterfaceNumber;
}
@@ -64,15 +72,15 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
return mNumEndpoints;
}
- public byte getUsbClass() {
+ public int getUsbClass() {
return mUsbClass;
}
- public byte getUsbSubclass() {
+ public int getUsbSubclass() {
return mUsbSubclass;
}
- public byte getProtocol() {
+ public int getProtocol() {
return mProtocol;
}
@@ -80,13 +88,29 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
return mDescrIndex;
}
+ void addEndpointDescriptor(UsbEndpointDescriptor endpoint) {
+ mEndpointDescriptors.add(endpoint);
+ }
+
+ UsbInterface toAndroid(UsbDescriptorParser parser) {
+ String name = parser.getDescriptorString(mDescrIndex);
+ UsbInterface ntrface = new UsbInterface(
+ mInterfaceNumber, mAlternateSetting, name, mUsbClass, mUsbSubclass, mProtocol);
+ UsbEndpoint[] endpoints = new UsbEndpoint[mEndpointDescriptors.size()];
+ for (int index = 0; index < mEndpointDescriptors.size(); index++) {
+ endpoints[index] = mEndpointDescriptors.get(index).toAndroid(parser);
+ }
+ ntrface.setEndpoints(endpoints);
+ return ntrface;
+ }
+
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
- byte usbClass = getUsbClass();
- byte usbSubclass = getUsbSubclass();
- byte protocol = getProtocol();
+ int usbClass = getUsbClass();
+ int usbSubclass = getUsbSubclass();
+ int protocol = getProtocol();
String className = UsbStrings.getClassName(usbClass);
String subclassName = "";
if (usbClass == UsbDescriptor.CLASSID_AUDIO) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
index 85a3e6802ff7..d0ca6db87d38 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
@@ -25,7 +25,7 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
public final class UsbMSMidiHeader extends UsbACInterface {
private static final String TAG = "UsbMSMidiHeader";
- public UsbMSMidiHeader(int length, byte type, byte subtype, byte subclass) {
+ public UsbMSMidiHeader(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
index 1d5cbf2b5c99..7df7cfc6a5e3 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
@@ -25,7 +25,7 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
public final class UsbMSMidiInputJack extends UsbACInterface {
private static final String TAG = "UsbMSMidiInputJack";
- UsbMSMidiInputJack(int length, byte type, byte subtype, byte subclass) {
+ UsbMSMidiInputJack(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
index 9f50240a94ca..1879ac096abd 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
@@ -25,7 +25,7 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
public final class UsbMSMidiOutputJack extends UsbACInterface {
private static final String TAG = "UsbMSMidiOutputJack";
- public UsbMSMidiOutputJack(int length, byte type, byte subtype, byte subclass) {
+ public UsbMSMidiOutputJack(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
index 99ebccade735..adfc5143bc09 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import android.hardware.usb.UsbDeviceConnection;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
/**
* @hide
@@ -32,8 +32,8 @@ public final class HTMLReportCanvas extends ReportCanvas {
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
- public HTMLReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
- super(connection);
+ public HTMLReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
+ super(parser);
mStringBuilder = stringBuilder;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
index 9e0adf55d87b..c34dc988555d 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import android.hardware.usb.UsbDeviceConnection;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
/**
* @hide
@@ -24,22 +24,19 @@ import android.hardware.usb.UsbDeviceConnection;
public abstract class ReportCanvas {
private static final String TAG = "ReportCanvas";
- private final UsbDeviceConnection mConnection;
+ private final UsbDescriptorParser mParser;
/**
* Constructor.
* @param connection The USB connection object used to retrieve strings
* from the USB device.
*/
- public ReportCanvas(UsbDeviceConnection connection) {
- mConnection = connection;
+ public ReportCanvas(UsbDescriptorParser parser) {
+ mParser = parser;
}
- /**
- * @returns the UsbDeviceConnection member (mConnection).
- */
- public UsbDeviceConnection getConnection() {
- return mConnection;
+ public UsbDescriptorParser getParser() {
+ return mParser;
}
/**
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
index a43569d40a67..1e19ea1447ef 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import android.hardware.usb.UsbDeviceConnection;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
/**
* @hide
@@ -34,8 +34,8 @@ public final class TextReportCanvas extends ReportCanvas {
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
- public TextReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
- super(connection);
+ public TextReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
+ super(parser);
mStringBuilder = stringBuilder;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
index 64ecebc29db6..fb4576a6ee78 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
@@ -32,8 +32,8 @@ public final class UsbStrings {
private static HashMap<Byte, String> sDescriptorNames;
private static HashMap<Byte, String> sACControlInterfaceNames;
private static HashMap<Byte, String> sACStreamingInterfaceNames;
- private static HashMap<Byte, String> sClassNames;
- private static HashMap<Byte, String> sAudioSubclassNames;
+ private static HashMap<Integer, String> sClassNames;
+ private static HashMap<Integer, String> sAudioSubclassNames;
private static HashMap<Integer, String> sAudioEncodingNames;
private static HashMap<Integer, String> sTerminalNames;
private static HashMap<Integer, String> sFormatNames;
@@ -92,7 +92,7 @@ public final class UsbStrings {
}
private static void initClassNames() {
- sClassNames = new HashMap<Byte, String>();
+ sClassNames = new HashMap<Integer, String>();
sClassNames.put(UsbDescriptor.CLASSID_DEVICE, "Device");
sClassNames.put(UsbDescriptor.CLASSID_AUDIO, "Audio");
sClassNames.put(UsbDescriptor.CLASSID_COM, "Communications");
@@ -118,7 +118,7 @@ public final class UsbStrings {
}
private static void initAudioSubclassNames() {
- sAudioSubclassNames = new HashMap<Byte, String>();
+ sAudioSubclassNames = new HashMap<Integer, String>();
sAudioSubclassNames.put(UsbDescriptor.AUDIO_SUBCLASS_UNDEFINED, "Undefinded");
sAudioSubclassNames.put(UsbDescriptor.AUDIO_AUDIOCONTROL, "Audio Control");
sAudioSubclassNames.put(UsbDescriptor.AUDIO_AUDIOSTREAMING, "Audio Streaming");
@@ -300,7 +300,7 @@ public final class UsbStrings {
/**
* Retrieves the name for the specified USB class ID.
*/
- public static String getClassName(byte classID) {
+ public static String getClassName(int classID) {
String name = sClassNames.get(classID);
int iClassID = classID & 0xFF;
return name != null
@@ -312,7 +312,7 @@ public final class UsbStrings {
/**
* Retrieves the name for the specified USB audio subclass ID.
*/
- public static String getAudioSubclassName(byte subClassID) {
+ public static String getAudioSubclassName(int subClassID) {
String name = sAudioSubclassNames.get(subClassID);
int iSubclassID = subClassID & 0xFF;
return name != null
@@ -335,7 +335,7 @@ public final class UsbStrings {
/**
* Retrieves the name for the specified USB audio interface subclass ID.
*/
- public static String getACInterfaceSubclassName(byte subClassID) {
+ public static String getACInterfaceSubclassName(int subClassID) {
return subClassID == UsbDescriptor.AUDIO_AUDIOCONTROL ? "AC Control" : "AC Streaming";
}
}
diff --git a/legacy-test/Android.mk b/test-base/Android.mk
index 4c150c8f8a23..f73eff7d63aa 100644
--- a/legacy-test/Android.mk
+++ b/test-base/Android.mk
@@ -63,8 +63,8 @@ LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src
LEGACY_TEST_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/api.txt
LEGACY_TEST_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/removed.txt
-LEGACY_TEST_API_FILE := $(LOCAL_PATH)/api/legacy-test-current.txt
-LEGACY_TEST_REMOVED_API_FILE := $(LOCAL_PATH)/api/legacy-test-removed.txt
+LEGACY_TEST_API_FILE := $(LOCAL_PATH)/api/android-test-base-current.txt
+LEGACY_TEST_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-base-removed.txt
LOCAL_DROIDDOC_OPTIONS:= \
-stubpackages android.test:android.test.suitebuilder.annotation:com.android.internal.util:junit.framework \
@@ -119,7 +119,7 @@ $(eval $(call check-api, \
-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
-error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
-error 25 -error 26 -error 27, \
- cat $(LOCAL_PATH)/api/apicheck_msg_legacy_test.txt, \
+ cat $(LOCAL_PATH)/api/apicheck_msg_android_test_base.txt, \
check-legacy-test-api, \
$(call doc-timestamp-for,legacy-test-api-stubs-gen) \
))
diff --git a/legacy-test/api/legacy-test-current.txt b/test-base/api/android-test-base-current.txt
index 7ebd6aa8a4a2..7ebd6aa8a4a2 100644
--- a/legacy-test/api/legacy-test-current.txt
+++ b/test-base/api/android-test-base-current.txt
diff --git a/legacy-test/api/legacy-test-removed.txt b/test-base/api/android-test-base-removed.txt
index e69de29bb2d1..e69de29bb2d1 100644
--- a/legacy-test/api/legacy-test-removed.txt
+++ b/test-base/api/android-test-base-removed.txt
diff --git a/legacy-test/api/apicheck_msg_legacy_test.txt b/test-base/api/apicheck_msg_android_test_base.txt
index ad5f2359b8b1..ad5f2359b8b1 100644
--- a/legacy-test/api/apicheck_msg_legacy_test.txt
+++ b/test-base/api/apicheck_msg_android_test_base.txt
diff --git a/legacy-test/jarjar-rules.txt b/test-base/jarjar-rules.txt
index fd8555c8931c..fd8555c8931c 100644
--- a/legacy-test/jarjar-rules.txt
+++ b/test-base/jarjar-rules.txt
diff --git a/legacy-test/src/android/test/AndroidTestCase.java b/test-base/src/android/test/AndroidTestCase.java
index 1e6bd9c14fd9..1e6bd9c14fd9 100644
--- a/legacy-test/src/android/test/AndroidTestCase.java
+++ b/test-base/src/android/test/AndroidTestCase.java
diff --git a/legacy-test/src/android/test/FlakyTest.java b/test-base/src/android/test/FlakyTest.java
index 4e5c4e35a8c6..4e5c4e35a8c6 100644
--- a/legacy-test/src/android/test/FlakyTest.java
+++ b/test-base/src/android/test/FlakyTest.java
diff --git a/legacy-test/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java
index 6b79314a4385..6b79314a4385 100644
--- a/legacy-test/src/android/test/InstrumentationTestCase.java
+++ b/test-base/src/android/test/InstrumentationTestCase.java
diff --git a/legacy-test/src/android/test/InstrumentationTestSuite.java b/test-base/src/android/test/InstrumentationTestSuite.java
index a53fa267f1e1..a53fa267f1e1 100644
--- a/legacy-test/src/android/test/InstrumentationTestSuite.java
+++ b/test-base/src/android/test/InstrumentationTestSuite.java
diff --git a/legacy-test/src/android/test/PerformanceTestCase.java b/test-base/src/android/test/PerformanceTestCase.java
index 65bd4a48f7f5..65bd4a48f7f5 100644
--- a/legacy-test/src/android/test/PerformanceTestCase.java
+++ b/test-base/src/android/test/PerformanceTestCase.java
diff --git a/legacy-test/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java
index 6a7130e68e61..6a7130e68e61 100644
--- a/legacy-test/src/android/test/RepetitiveTest.java
+++ b/test-base/src/android/test/RepetitiveTest.java
diff --git a/legacy-test/src/android/test/UiThreadTest.java b/test-base/src/android/test/UiThreadTest.java
index cd06ab890074..cd06ab890074 100644
--- a/legacy-test/src/android/test/UiThreadTest.java
+++ b/test-base/src/android/test/UiThreadTest.java
diff --git a/legacy-test/src/android/test/package.html b/test-base/src/android/test/package.html
index 5be51359630e..5be51359630e 100644
--- a/legacy-test/src/android/test/package.html
+++ b/test-base/src/android/test/package.html
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java b/test-base/src/android/test/suitebuilder/annotation/LargeTest.java
index dc77ee6b2739..dc77ee6b2739 100644
--- a/legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java
+++ b/test-base/src/android/test/suitebuilder/annotation/LargeTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java b/test-base/src/android/test/suitebuilder/annotation/MediumTest.java
index b941da03ac9a..b941da03ac9a 100644
--- a/legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java
+++ b/test-base/src/android/test/suitebuilder/annotation/MediumTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java b/test-base/src/android/test/suitebuilder/annotation/SmallTest.java
index d3c74f019b53..d3c74f019b53 100644
--- a/legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java
+++ b/test-base/src/android/test/suitebuilder/annotation/SmallTest.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Smoke.java b/test-base/src/android/test/suitebuilder/annotation/Smoke.java
index aac293796be1..aac293796be1 100644
--- a/legacy-test/src/android/test/suitebuilder/annotation/Smoke.java
+++ b/test-base/src/android/test/suitebuilder/annotation/Smoke.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Suppress.java b/test-base/src/android/test/suitebuilder/annotation/Suppress.java
index 629a3cf4a2cd..629a3cf4a2cd 100644
--- a/legacy-test/src/android/test/suitebuilder/annotation/Suppress.java
+++ b/test-base/src/android/test/suitebuilder/annotation/Suppress.java
diff --git a/legacy-test/src/android/test/suitebuilder/annotation/package.html b/test-base/src/android/test/suitebuilder/annotation/package.html
index ffba2e9bf980..ffba2e9bf980 100644
--- a/legacy-test/src/android/test/suitebuilder/annotation/package.html
+++ b/test-base/src/android/test/suitebuilder/annotation/package.html
diff --git a/legacy-test/src/com/android/internal/util/Predicate.java b/test-base/src/com/android/internal/util/Predicate.java
index e87f489f4670..e87f489f4670 100644
--- a/legacy-test/src/com/android/internal/util/Predicate.java
+++ b/test-base/src/com/android/internal/util/Predicate.java
diff --git a/legacy-test/src/junit/MODULE_LICENSE_CPL b/test-base/src/junit/MODULE_LICENSE_CPL
index e69de29bb2d1..e69de29bb2d1 100644
--- a/legacy-test/src/junit/MODULE_LICENSE_CPL
+++ b/test-base/src/junit/MODULE_LICENSE_CPL
diff --git a/legacy-test/src/junit/README.android b/test-base/src/junit/README.android
index 1384a1fedda2..1384a1fedda2 100644
--- a/legacy-test/src/junit/README.android
+++ b/test-base/src/junit/README.android
diff --git a/legacy-test/src/junit/cpl-v10.html b/test-base/src/junit/cpl-v10.html
index 36aa208d4a29..36aa208d4a29 100644
--- a/legacy-test/src/junit/cpl-v10.html
+++ b/test-base/src/junit/cpl-v10.html
diff --git a/legacy-test/src/junit/framework/Assert.java b/test-base/src/junit/framework/Assert.java
index 3dcc23d71c19..3dcc23d71c19 100644
--- a/legacy-test/src/junit/framework/Assert.java
+++ b/test-base/src/junit/framework/Assert.java
diff --git a/legacy-test/src/junit/framework/AssertionFailedError.java b/test-base/src/junit/framework/AssertionFailedError.java
index 0d7802c431c6..0d7802c431c6 100644
--- a/legacy-test/src/junit/framework/AssertionFailedError.java
+++ b/test-base/src/junit/framework/AssertionFailedError.java
diff --git a/legacy-test/src/junit/framework/ComparisonCompactor.java b/test-base/src/junit/framework/ComparisonCompactor.java
index e540f03b87d3..e540f03b87d3 100644
--- a/legacy-test/src/junit/framework/ComparisonCompactor.java
+++ b/test-base/src/junit/framework/ComparisonCompactor.java
diff --git a/legacy-test/src/junit/framework/ComparisonFailure.java b/test-base/src/junit/framework/ComparisonFailure.java
index 507799328a44..507799328a44 100644
--- a/legacy-test/src/junit/framework/ComparisonFailure.java
+++ b/test-base/src/junit/framework/ComparisonFailure.java
diff --git a/legacy-test/src/junit/framework/Protectable.java b/test-base/src/junit/framework/Protectable.java
index e1432370cfaf..e1432370cfaf 100644
--- a/legacy-test/src/junit/framework/Protectable.java
+++ b/test-base/src/junit/framework/Protectable.java
diff --git a/legacy-test/src/junit/framework/Test.java b/test-base/src/junit/framework/Test.java
index a016ee8308f1..a016ee8308f1 100644
--- a/legacy-test/src/junit/framework/Test.java
+++ b/test-base/src/junit/framework/Test.java
diff --git a/legacy-test/src/junit/framework/TestCase.java b/test-base/src/junit/framework/TestCase.java
index b047ec9e1afc..b047ec9e1afc 100644
--- a/legacy-test/src/junit/framework/TestCase.java
+++ b/test-base/src/junit/framework/TestCase.java
diff --git a/legacy-test/src/junit/framework/TestFailure.java b/test-base/src/junit/framework/TestFailure.java
index 6662b1fab1b2..6662b1fab1b2 100644
--- a/legacy-test/src/junit/framework/TestFailure.java
+++ b/test-base/src/junit/framework/TestFailure.java
diff --git a/legacy-test/src/junit/framework/TestListener.java b/test-base/src/junit/framework/TestListener.java
index 9b6944361b9d..9b6944361b9d 100644
--- a/legacy-test/src/junit/framework/TestListener.java
+++ b/test-base/src/junit/framework/TestListener.java
diff --git a/legacy-test/src/junit/framework/TestResult.java b/test-base/src/junit/framework/TestResult.java
index 3052e94074fd..3052e94074fd 100644
--- a/legacy-test/src/junit/framework/TestResult.java
+++ b/test-base/src/junit/framework/TestResult.java
diff --git a/legacy-test/src/junit/framework/TestSuite.java b/test-base/src/junit/framework/TestSuite.java
index 336efd1800d7..336efd1800d7 100644
--- a/legacy-test/src/junit/framework/TestSuite.java
+++ b/test-base/src/junit/framework/TestSuite.java
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
index 18da8b8bd687..e4af17cbd711 100644
--- a/test-mock/Android.mk
+++ b/test-mock/Android.mk
@@ -26,7 +26,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt
LOCAL_MODULE:= repackaged.android.test.mock
@@ -130,15 +130,4 @@ update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP)
@echo Copying removed.txt
$(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
-# Build the android.test.mock.sdk library
-# =======================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android.test.mock.sdk
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := android.test.mock.stubs
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index d0f5b3248c29..c0fd7f8ec26d 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -46,7 +46,7 @@ LOCAL_JAVA_LIBRARIES := \
legacy-test \
android.test.mock \
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt
LOCAL_MODULE:= repackaged.android.test.runner
diff --git a/test-runner/api/android-test-runner-current.txt b/test-runner/api/android-test-runner-current.txt
index 905cfe701ab6..1170eb53ab7f 100644
--- a/test-runner/api/android-test-runner-current.txt
+++ b/test-runner/api/android-test-runner-current.txt
@@ -271,8 +271,6 @@ package android.test.suitebuilder {
public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
- method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
- method public final android.test.suitebuilder.TestSuiteBuilder addRequirements(com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>...);
method public final junit.framework.TestSuite build();
method public android.test.suitebuilder.TestSuiteBuilder excludePackages(java.lang.String...);
method protected java.lang.String getSuiteName();
diff --git a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
index 6158e0cf14f4..2857696ef2ff 100644
--- a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
+++ b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
@@ -119,6 +119,7 @@ public class TestSuiteBuilder {
*
* @param predicates Predicates to add to the list of requirements.
* @return The builder for method chaining.
+ * @hide
*/
public TestSuiteBuilder addRequirements(List<Predicate<TestMethod>> predicates) {
this.predicates.addAll(predicates);
@@ -156,7 +157,7 @@ public class TestSuiteBuilder {
/**
* Override the default name for the suite being built. This should generally be called if you
- * call {@link #addRequirements(com.android.internal.util.Predicate[])} to make it clear which
+ * call {@code addRequirements(com.android.internal.util.Predicate[])} to make it clear which
* tests will be included. The name you specify is automatically prefixed with the package
* containing the tests to be run. If more than one package is specified, the first is used.
*
@@ -215,6 +216,7 @@ public class TestSuiteBuilder {
*
* @param predicates Predicates to add to the list of requirements.
* @return The builder for method chaining.
+ * @hide
*/
public final TestSuiteBuilder addRequirements(Predicate<TestMethod>... predicates) {
ArrayList<Predicate<TestMethod>> list = new ArrayList<Predicate<TestMethod>>();
diff --git a/tests/testables/src/android/testing/TestableResources.java b/tests/testables/src/android/testing/TestableResources.java
index a2fa95deaa60..c60f07d56d92 100644
--- a/tests/testables/src/android/testing/TestableResources.java
+++ b/tests/testables/src/android/testing/TestableResources.java
@@ -39,7 +39,8 @@ public class TestableResources {
private final Resources mResources;
private final SparseArray<Object> mOverrides = new SparseArray<>();
- TestableResources(Resources realResources) {
+ /** Creates a TestableResources instance that calls through to the given real Resources. */
+ public TestableResources(Resources realResources) {
mResources = mock(Resources.class, withSettings()
.spiedInstance(realResources)
.defaultAnswer(this::answer));
diff --git a/wifi/java/android/net/wifi/BatchedScanResult.java b/wifi/java/android/net/wifi/BatchedScanResult.java
index 6d9f00f3d5ca..c06543ec260a 100644
--- a/wifi/java/android/net/wifi/BatchedScanResult.java
+++ b/wifi/java/android/net/wifi/BatchedScanResult.java
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.os.Parcelable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import java.util.ArrayList;
@@ -29,6 +30,7 @@ import java.util.List;
* @removed
*/
@Deprecated
+@SystemApi
public class BatchedScanResult implements Parcelable {
private static final String TAG = "BatchedScanResult";
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index a9e1e9d06470..abbb82a9cc69 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -21,6 +21,7 @@ import android.content.pm.ParceledListSlice;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
@@ -184,5 +185,7 @@ interface IWifiManager
void restoreBackupData(in byte[] data);
void restoreSupplicantBackupData(in byte[] supplicantData, in byte[] ipConfigData);
+
+ void startSubscriptionProvisioning(in OsuProvider provider, in IProvisioningCallback callback);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 183cf0d10d49..558004ce1753 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -33,6 +33,8 @@ import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.IProvisioningCallback;
+import android.net.wifi.hotspot2.ProvisioningCallback;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -3610,4 +3612,45 @@ public class WifiManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Start subscription provisioning flow
+ * @param provider {@link OsuProvider} to provision with
+ * @param callback {@link ProvisioningCallback} for updates regarding provisioning flow
+ * @hide
+ */
+ public void startSubscriptionProvisioning(OsuProvider provider, ProvisioningCallback callback,
+ @Nullable Handler handler) {
+ Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
+ try {
+ mService.startSubscriptionProvisioning(provider,
+ new ProvisioningCallbackProxy(looper, callback));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static class ProvisioningCallbackProxy extends IProvisioningCallback.Stub {
+ private final Handler mHandler;
+ private final ProvisioningCallback mCallback;
+
+ ProvisioningCallbackProxy(Looper looper, ProvisioningCallback callback) {
+ mHandler = new Handler(looper);
+ mCallback = callback;
+ }
+
+ @Override
+ public void onProvisioningStatus(int status) {
+ mHandler.post(() -> {
+ mCallback.onProvisioningStatus(status);
+ });
+ }
+
+ @Override
+ public void onProvisioningFailure(int status) {
+ mHandler.post(() -> {
+ mCallback.onProvisioningFailure(status);
+ });
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl b/wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl
new file mode 100644
index 000000000000..c2cb16ab847c
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.net.wifi.hotspot2;
+
+/**
+ * Interface for Provisioning callback.
+ *
+ * @hide
+ */
+oneway interface IProvisioningCallback
+{
+ /**
+ * Service to manager callback providing failure notification
+ */
+ void onProvisioningFailure(int status);
+
+ /**
+ * Service to manager callback providing Provisioning status
+ */
+ void onProvisioningStatus(int status);
+}
+
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
new file mode 100644
index 000000000000..8b86cdde4a9e
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -0,0 +1,59 @@
+/*
+ * 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.net.wifi.hotspot2;
+
+import android.os.Handler;
+
+/**
+ * Base class for provisioning callbacks. Should be extended by applications and set when calling
+ * {@link WifiManager#startSubscriptionProvisiong(OsuProvider, ProvisioningCallback, Handler)}.
+ *
+ * @hide
+ */
+public abstract class ProvisioningCallback {
+
+ /**
+ * The reason code for Provisioning Failure due to connection failure to OSU AP.
+ * @hide
+ */
+ public static final int OSU_FAILURE_AP_CONNECTION = 1;
+
+ /**
+ * The status code for Provisioning flow to indicate connecting to OSU AP
+ * @hide
+ */
+ public static final int OSU_STATUS_AP_CONNECTING = 1;
+
+ /**
+ * The status code for Provisioning flow to indicate connected to OSU AP
+ * @hide
+ */
+ public static final int OSU_STATUS_AP_CONNECTED = 2;
+
+ /**
+ * Provisioning status for OSU failure
+ * @param status indicates error condition
+ */
+ public abstract void onProvisioningFailure(int status);
+
+ /**
+ * Provisioning status when OSU is in progress
+ * @param status indicates status of OSU flow
+ */
+ public abstract void onProvisioningStatus(int status);
+}
+