summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--Android.mk5
-rw-r--r--api/current.txt6
-rw-r--r--api/system-current.txt6
-rw-r--r--api/system-removed.txt1
-rw-r--r--api/test-current.txt11
-rw-r--r--cmds/incidentd/tests/Reporter_test.cpp14
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java890
-rw-r--r--cmds/statsd/Android.mk6
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp40
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h33
-rw-r--r--cmds/statsd/src/StatsService.cpp38
-rw-r--r--cmds/statsd/src/StatsService.h32
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp2
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp19
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp22
-rw-r--r--cmds/statsd/src/logd/LogEvent.h8
-rw-r--r--cmds/statsd/src/metrics/CountAnomalyTracker.cpp56
-rw-r--r--cmds/statsd/src/metrics/CountAnomalyTracker.h16
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp76
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h11
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp56
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h7
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp132
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h70
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp67
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h22
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp2
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h10
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp178
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h5
-rw-r--r--cmds/statsd/src/stats_events.proto540
-rw-r--r--cmds/statsd/src/stats_util.h8
-rw-r--r--cmds/statsd/src/statsd_config.proto2
-rw-r--r--config/compiled-classes-phone12
-rw-r--r--core/java/Android.bp4
-rw-r--r--core/java/android/app/ActivityManager.java59
-rw-r--r--core/java/android/app/AlarmManager.java23
-rw-r--r--core/java/android/app/PendingIntent.java12
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java46
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/job/JobInfo.java21
-rw-r--r--core/java/android/app/job/JobParameters.java38
-rw-r--r--core/java/android/app/slice/SliceProvider.java43
-rw-r--r--core/java/android/app/usage/AppStandby.java83
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl2
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java24
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java81
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java46
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java (renamed from core/java/android/bluetooth/BluetoothInputHost.java)44
-rw-r--r--core/java/android/bluetooth/BluetoothHidHost.java (renamed from core/java/android/bluetooth/BluetoothInputDevice.java)70
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java8
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java106
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java9
-rw-r--r--core/java/android/content/pm/PackageParser.java40
-rw-r--r--core/java/android/hardware/usb/UsbManager.java5
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java42
-rw-r--r--core/java/android/net/NetworkCapabilities.java87
-rw-r--r--core/java/android/net/NetworkRequest.java22
-rw-r--r--core/java/android/net/metrics/ConnectStats.java11
-rw-r--r--core/java/android/net/metrics/DefaultNetworkEvent.java79
-rw-r--r--core/java/android/net/metrics/DnsEvent.java21
-rw-r--r--core/java/android/net/metrics/NetworkMetrics.java168
-rw-r--r--core/java/android/os/BatteryStats.java4
-rw-r--r--core/java/android/os/Binder.java4
-rw-r--r--core/java/android/os/IStatsCallbacks.aidl (renamed from core/java/android/view/autofill/AutoFillType.aidl)13
-rw-r--r--core/java/android/os/IStatsManager.aidl13
-rw-r--r--core/java/android/os/ShellCommand.java37
-rw-r--r--core/java/android/os/StrictMode.java228
-rwxr-xr-xcore/java/android/provider/Settings.java9
-rw-r--r--core/java/android/security/NetworkSecurityPolicy.java4
-rw-r--r--core/java/android/security/net/config/NetworkSecurityConfig.java6
-rw-r--r--core/java/android/text/Layout.java2
-rw-r--r--core/java/android/text/MeasuredText.java43
-rw-r--r--core/java/android/text/StaticLayout.java613
-rw-r--r--core/java/android/text/TextLine.java38
-rw-r--r--core/java/android/text/TextUtils.java4
-rw-r--r--core/java/android/view/TouchDelegate.java46
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java46
-rw-r--r--core/java/android/view/textclassifier/TextClassifier.java8
-rw-r--r--core/java/android/webkit/WebViewFactory.java28
-rw-r--r--core/java/android/webkit/WebViewLibraryLoader.java232
-rw-r--r--core/java/android/widget/Editor.java183
-rw-r--r--core/java/android/widget/TextView.java30
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java138
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java101
-rw-r--r--core/java/com/android/internal/util/LocalLog.java13
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java41
-rw-r--r--core/java/com/android/internal/widget/Magnifier.java148
-rw-r--r--core/jni/android/graphics/FontFamily.cpp4
-rw-r--r--core/jni/android/graphics/Paint.cpp2
-rw-r--r--core/jni/android_database_SQLiteConnection.cpp8
-rw-r--r--core/jni/android_os_HwBinder.cpp61
-rw-r--r--core/jni/android_text_Hyphenator.cpp113
-rw-r--r--core/jni/android_text_StaticLayout.cpp294
-rw-r--r--core/jni/android_util_Binder.cpp62
-rw-r--r--core/proto/android/app/activitymanager.proto80
-rw-r--r--core/proto/android/app/alarmmanager.proto54
-rw-r--r--core/proto/android/app/pendingintent.proto28
-rw-r--r--core/proto/android/content/intent.proto26
-rw-r--r--core/proto/android/internal/locallog.proto24
-rw-r--r--core/proto/android/os/batterymanager.proto29
-rw-r--r--core/proto/android/os/incident.proto14
-rw-r--r--core/proto/android/os/powermanager.proto84
-rw-r--r--core/proto/android/providers/settings.proto8
-rw-r--r--core/proto/android/server/activitymanagerservice.proto11
-rw-r--r--core/proto/android/server/alarmmanagerservice.proto247
-rw-r--r--core/proto/android/server/powermanagerservice.proto (renamed from core/proto/android/service/power.proto)141
-rw-r--r--core/proto/android/server/wirelesschargerdetector.proto (renamed from core/proto/android/service/wirelesschargerdetector.proto)4
-rw-r--r--core/proto/android/service/battery.proto10
-rw-r--r--core/proto/android/view/display.proto41
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/layout/magnifier.xml20
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/res/res/values/attrs_manifest.xml2
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java11
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java6
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java2
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java125
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java78
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java6
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java34
-rw-r--r--core/tests/coretests/Android.mk7
-rw-r--r--core/tests/coretests/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/BinderProxyCountingTestApp/Android.mk27
-rw-r--r--core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml23
-rw-r--r--core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java182
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/Android.mk27
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml25
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java53
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java101
-rw-r--r--core/tests/coretests/aidl/Android.mk22
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl23
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl21
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl30
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl26
-rw-r--r--core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestRemoteCallback.aidl (renamed from core/java/android/service/autofill/Dataset.aidl)11
-rw-r--r--core/tests/coretests/src/android/os/BinderProxyCountingTest.java378
-rw-r--r--core/tests/coretests/src/android/text/TextLineTest.java67
-rw-r--r--core/tests/webkit/Android.mk44
-rw-r--r--core/tests/webkit/AndroidManifest.xml30
-rw-r--r--core/tests/webkit/AndroidTest.xml29
-rw-r--r--core/tests/webkit/apk_with_native_libs/Android.mk66
-rw-r--r--core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml29
-rw-r--r--core/tests/webkit/apk_with_native_libs/jni/Android.mk32
-rw-r--r--core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp (renamed from core/java/android/service/autofill/SaveInfo.aidl)12
-rw-r--r--core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml28
-rw-r--r--core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java (renamed from core/java/android/view/autofill/AutoFillValue.aidl)16
-rw-r--r--core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java329
-rw-r--r--data/etc/privapp-permissions-platform.xml6
-rw-r--r--legacy-test/Android.mk4
-rw-r--r--libs/hwui/renderstate/RenderState.cpp2
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h4
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp5
-rw-r--r--location/java/android/location/GnssClock.java3
-rw-r--r--packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java28
-rw-r--r--packages/SettingsLib/OWNERS21
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java7
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java10
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java36
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java120
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java7
-rw-r--r--proto/Android.bp17
-rw-r--r--proto/Android.mk33
-rw-r--r--proto/src/metrics_constants.proto10
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java92
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java7
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformBackupTask.java2
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java5
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java395
-rw-r--r--services/core/java/com/android/server/BatteryService.java16
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java30
-rw-r--r--services/core/java/com/android/server/IpSecService.java6
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java12
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java168
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java97
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java17
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java537
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java227
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java72
-rw-r--r--services/core/java/com/android/server/am/AppTaskImpl.java10
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java12
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java21
-rw-r--r--services/core/java/com/android/server/am/LaunchingActivityPositioner.java62
-rw-r--r--services/core/java/com/android/server/am/LaunchingBoundsController.java160
-rw-r--r--services/core/java/com/android/server/am/LaunchingTaskPositioner.java100
-rw-r--r--services/core/java/com/android/server/am/LockTaskController.java2
-rw-r--r--services/core/java/com/android/server/am/LockTaskNotify.java25
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java77
-rw-r--r--services/core/java/com/android/server/am/UserController.java15
-rw-r--r--services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java78
-rw-r--r--services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java27
-rw-r--r--services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java24
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java171
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java58
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java99
-rw-r--r--services/core/java/com/android/server/job/JobServiceContext.java2
-rw-r--r--services/core/java/com/android/server/job/controllers/ConnectivityController.java5
-rw-r--r--services/core/java/com/android/server/job/controllers/JobStatus.java5
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java8
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java959
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java1920
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java8
-rw-r--r--services/core/java/com/android/server/pm/Settings.java2
-rw-r--r--services/core/java/com/android/server/pm/UserDataPreparer.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java34
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java1000
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionSettings.java12
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java141
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java40
-rw-r--r--services/core/java/com/android/server/power/WirelessChargerDetector.java1
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java28
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java306
-rw-r--r--services/core/java/com/android/server/wm/DragState.java197
-rw-r--r--services/core/java/com/android/server/wm/Session.java200
-rw-r--r--services/core/java/com/android/server/wm/StackWindowController.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java117
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java30
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/net/java/android/net/util/VersionedBroadcastListener.java113
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java154
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java230
-rw-r--r--services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java143
-rw-r--r--services/tests/servicestests/src/com/android/server/am/LaunchingBoundsControllerTests.java173
-rw-r--r--services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java (renamed from services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java)72
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java57
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java322
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java6
-rw-r--r--services/usage/java/com/android/server/usage/AppIdleHistory.java239
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java403
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java99
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java44
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java21
-rw-r--r--telephony/java/android/telephony/DisconnectCause.java9
-rw-r--r--telephony/java/android/telephony/SmsManager.java15
-rw-r--r--telephony/java/android/telephony/SmsMessage.java20
-rw-r--r--test-runner/Android.mk4
-rw-r--r--tests/net/java/android/net/util/VersionedBroadcastListenerTest.java131
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java13
-rw-r--r--tests/net/java/com/android/server/IpSecServiceTest.java2
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java21
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java251
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java2
-rw-r--r--tools/locked_region_code_injection/Android.bp12
-rw-r--r--tools/locked_region_code_injection/Android.mk15
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java2
-rw-r--r--tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl4
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java65
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java10
278 files changed, 13473 insertions, 6470 deletions
diff --git a/Android.bp b/Android.bp
index 2dc1cc3c19fe..c89cc400171d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -72,9 +72,10 @@ cc_library {
subdirs = [
"cmds/*",
- "core/jni",
+ "core/*",
"libs/*",
"media/*",
+ "proto",
"tools/*",
"native/android",
"native/graphics/jni",
diff --git a/Android.mk b/Android.mk
index c87aec40a0ca..9890bb401404 100644
--- a/Android.mk
+++ b/Android.mk
@@ -128,7 +128,7 @@ LOCAL_SRC_FILES += \
../../system/bt/binder/android/bluetooth/IBluetoothHeadsetPhone.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHealth.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHealthCallback.aidl \
- ../../system/bt/binder/android/bluetooth/IBluetoothInputDevice.aidl \
+ ../../system/bt/binder/android/bluetooth/IBluetoothHidHost.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothPan.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothManager.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothManagerCallback.aidl \
@@ -139,7 +139,7 @@ LOCAL_SRC_FILES += \
../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \
- ../../system/bt/binder/android/bluetooth/IBluetoothInputHost.aidl \
+ ../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHidDeviceCallback.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothGatt.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothGattCallback.aidl \
@@ -270,6 +270,7 @@ LOCAL_SRC_FILES += \
core/java/android/os/IRecoverySystemProgressListener.aidl \
core/java/android/os/IRemoteCallback.aidl \
core/java/android/os/ISchedulingPolicyService.aidl \
+ core/java/android/os/IStatsCallbacks.aidl \
core/java/android/os/IStatsCompanionService.aidl \
core/java/android/os/IStatsManager.aidl \
core/java/android/os/IThermalEventListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index df8b7ef63bae..96fbd31af65b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6451,6 +6451,8 @@ package android.app.admin {
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
@@ -6897,6 +6899,7 @@ package android.app.job {
method public int getClipGrantFlags();
method public android.os.PersistableBundle getExtras();
method public int getJobId();
+ method public android.net.Network getNetwork();
method public android.os.Bundle getTransientExtras();
method public java.lang.String[] getTriggeredContentAuthorities();
method public android.net.Uri[] getTriggeredContentUris();
@@ -40443,6 +40446,8 @@ package android.telephony {
field public static final int ENCODING_7BIT = 1; // 0x1
field public static final int ENCODING_8BIT = 2; // 0x2
field public static final int ENCODING_UNKNOWN = 0; // 0x0
+ field public static final java.lang.String FORMAT_3GPP = "3gpp";
+ field public static final java.lang.String FORMAT_3GPP2 = "3gpp2";
field public static final int MAX_USER_DATA_BYTES = 140; // 0x8c
field public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; // 0x86
field public static final int MAX_USER_DATA_SEPTETS = 160; // 0xa0
@@ -52070,7 +52075,6 @@ package android.widget {
method public android.graphics.Typeface getTypeface();
method public android.text.style.URLSpan[] getUrls();
method public boolean hasSelection();
- method public void invalidate(int, int, int, int);
method public boolean isAllCaps();
method public boolean isCursorVisible();
method public boolean isElegantTextHeight();
diff --git a/api/system-current.txt b/api/system-current.txt
index 007316fcf524..4f2c2ba7c2b6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6686,6 +6686,8 @@ package android.app.admin {
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
@@ -7339,6 +7341,7 @@ package android.app.job {
method public int getClipGrantFlags();
method public android.os.PersistableBundle getExtras();
method public int getJobId();
+ method public android.net.Network getNetwork();
method public android.os.Bundle getTransientExtras();
method public java.lang.String[] getTriggeredContentAuthorities();
method public android.net.Uri[] getTriggeredContentUris();
@@ -43963,6 +43966,8 @@ package android.telephony {
field public static final int ENCODING_7BIT = 1; // 0x1
field public static final int ENCODING_8BIT = 2; // 0x2
field public static final int ENCODING_UNKNOWN = 0; // 0x0
+ field public static final java.lang.String FORMAT_3GPP = "3gpp";
+ field public static final java.lang.String FORMAT_3GPP2 = "3gpp2";
field public static final int MAX_USER_DATA_BYTES = 140; // 0x8c
field public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; // 0x86
field public static final int MAX_USER_DATA_SEPTETS = 160; // 0xa0
@@ -56174,7 +56179,6 @@ package android.widget {
method public android.graphics.Typeface getTypeface();
method public android.text.style.URLSpan[] getUrls();
method public boolean hasSelection();
- method public void invalidate(int, int, int, int);
method public boolean isAllCaps();
method public boolean isCursorVisible();
method public boolean isElegantTextHeight();
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 639877fae6e2..e9afa701c755 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -330,7 +330,6 @@ package android.net.wifi {
method public deprecated java.util.List<android.net.wifi.BatchedScanResult> getBatchedScanResults();
method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics();
method public deprecated boolean isBatchedScanSupported();
- method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index faccb6b58a3c..7930c47529b9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6519,6 +6519,8 @@ package android.app.admin {
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
@@ -6968,6 +6970,7 @@ package android.app.job {
method public int getClipGrantFlags();
method public android.os.PersistableBundle getExtras();
method public int getJobId();
+ method public android.net.Network getNetwork();
method public android.os.Bundle getTransientExtras();
method public java.lang.String[] getTriggeredContentAuthorities();
method public android.net.Uri[] getTriggeredContentUris();
@@ -32044,17 +32047,16 @@ package android.os {
public static final class StrictMode.ViolationInfo implements android.os.Parcelable {
ctor public StrictMode.ViolationInfo();
ctor public StrictMode.ViolationInfo(java.lang.Throwable, int);
- ctor public StrictMode.ViolationInfo(java.lang.String, java.lang.Throwable, int);
ctor public StrictMode.ViolationInfo(android.os.Parcel);
ctor public StrictMode.ViolationInfo(android.os.Parcel, boolean);
method public int describeContents();
method public void dump(android.util.Printer, java.lang.String);
+ method public java.lang.String getStackTrace();
+ method public java.lang.String getViolationDetails();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.StrictMode.ViolationInfo> CREATOR;
field public java.lang.String broadcastIntentAction;
- field public final android.app.ApplicationErrorReport.CrashInfo crashInfo;
field public int durationMillis;
- field public final java.lang.String message;
field public int numAnimationsRunning;
field public long numInstances;
field public final int policy;
@@ -40838,6 +40840,8 @@ package android.telephony {
field public static final int ENCODING_7BIT = 1; // 0x1
field public static final int ENCODING_8BIT = 2; // 0x2
field public static final int ENCODING_UNKNOWN = 0; // 0x0
+ field public static final java.lang.String FORMAT_3GPP = "3gpp";
+ field public static final java.lang.String FORMAT_3GPP2 = "3gpp2";
field public static final int MAX_USER_DATA_BYTES = 140; // 0x8c
field public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; // 0x86
field public static final int MAX_USER_DATA_SEPTETS = 160; // 0xa0
@@ -52678,7 +52682,6 @@ package android.widget {
method public android.graphics.Typeface getTypeface();
method public android.text.style.URLSpan[] getUrls();
method public boolean hasSelection();
- method public void invalidate(int, int, int, int);
method public boolean isAllCaps();
method public boolean isCursorVisible();
method public boolean isElegantTextHeight();
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index 5d074bcb0e4c..8d99fc7dbbbc 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -32,8 +32,6 @@ using namespace android::binder;
using namespace std;
using ::testing::StrEq;
using ::testing::Test;
-using ::testing::internal::CaptureStdout;
-using ::testing::internal::GetCapturedStdout;
class TestListener : public IIncidentReportStatusListener
{
@@ -139,20 +137,24 @@ TEST_F(ReporterTest, RunReportEmpty) {
}
TEST_F(ReporterTest, RunReportWithHeaders) {
+ TemporaryFile tf;
IncidentReportArgs args1, args2;
args1.addSection(1);
args2.addSection(2);
std::vector<int8_t> header {'a', 'b', 'c', 'd', 'e'};
args2.addHeader(header);
- sp<ReportRequest> r1 = new ReportRequest(args1, l, STDOUT_FILENO);
- sp<ReportRequest> r2 = new ReportRequest(args2, l, STDOUT_FILENO);
+ sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd);
+ sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd);
reporter->batch.add(r1);
reporter->batch.add(r2);
- CaptureStdout();
ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
- EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "abcde"));
+
+ string result;
+ ReadFileToString(tf.path, &result);
+ EXPECT_THAT(result, StrEq("\n\x5" "abcde"));
+
EXPECT_EQ(l->startInvoked, 2);
EXPECT_EQ(l->finishInvoked, 2);
EXPECT_TRUE(l->startSections.empty());
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 60ec8a96f325..29433f3fc14f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -116,9 +116,8 @@ public final class Pm {
}
public int run(String[] args) throws RemoteException {
- boolean validCommand = false;
if (args.length < 1) {
- return showUsage();
+ return runShellCommand("package", mArgs);
}
mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
@@ -134,18 +133,6 @@ public final class Pm {
String op = args[0];
mNextArg = 1;
- if ("list".equals(op)) {
- return runList();
- }
-
- if ("path".equals(op)) {
- return runPath();
- }
-
- if ("dump".equals(op)) {
- return runDump();
- }
-
if ("install".equals(op)) {
return runInstall();
}
@@ -166,134 +153,7 @@ public final class Pm {
return runInstallAbandon();
}
- if ("set-installer".equals(op)) {
- return runSetInstaller();
- }
-
- if ("uninstall".equals(op)) {
- return runUninstall();
- }
-
- if ("clear".equals(op)) {
- return runClear();
- }
-
- if ("enable".equals(op)) {
- return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- }
-
- if ("disable".equals(op)) {
- return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
- }
-
- if ("disable-user".equals(op)) {
- return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
- }
-
- if ("disable-until-used".equals(op)) {
- return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
- }
-
- if ("default-state".equals(op)) {
- return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
- }
-
- if ("hide".equals(op)) {
- return runSetHiddenSetting(true);
- }
-
- if ("unhide".equals(op)) {
- return runSetHiddenSetting(false);
- }
-
- if ("grant".equals(op)) {
- return runGrantRevokePermission(true);
- }
-
- if ("revoke".equals(op)) {
- return runGrantRevokePermission(false);
- }
-
- if ("reset-permissions".equals(op)) {
- return runResetPermissions();
- }
-
- if ("set-permission-enforced".equals(op)) {
- return runSetPermissionEnforced();
- }
-
- if ("set-app-link".equals(op)) {
- return runSetAppLink();
- }
-
- if ("get-app-link".equals(op)) {
- return runGetAppLink();
- }
-
- if ("set-install-location".equals(op)) {
- return runSetInstallLocation();
- }
-
- if ("get-install-location".equals(op)) {
- return runGetInstallLocation();
- }
-
- if ("trim-caches".equals(op)) {
- return runTrimCaches();
- }
-
- if ("create-user".equals(op)) {
- return runCreateUser();
- }
-
- if ("remove-user".equals(op)) {
- return runRemoveUser();
- }
-
- if ("get-max-users".equals(op)) {
- return runGetMaxUsers();
- }
-
- if ("force-dex-opt".equals(op)) {
- return runForceDexOpt();
- }
-
- if ("move-package".equals(op)) {
- return runMovePackage();
- }
-
- if ("move-primary-storage".equals(op)) {
- return runMovePrimaryStorage();
- }
-
- if ("set-user-restriction".equals(op)) {
- return runSetUserRestriction();
- }
-
- try {
- if (args.length == 1) {
- if (args[0].equalsIgnoreCase("-l")) {
- validCommand = true;
- return runShellCommand("package", new String[] { "list", "package" });
- } else if (args[0].equalsIgnoreCase("-lf")) {
- validCommand = true;
- return runShellCommand("package", new String[] { "list", "package", "-f" });
- }
- } else if (args.length == 2) {
- if (args[0].equalsIgnoreCase("-p")) {
- validCommand = true;
- return displayPackageFilePath(args[1], UserHandle.USER_SYSTEM);
- }
- }
- return 1;
- } finally {
- if (validCommand == false) {
- if (op != null) {
- System.err.println("Error: unknown command '" + op + "'");
- }
- showUsage();
- }
- }
+ return runShellCommand("package", mArgs);
}
static final class MyShellCallback extends ShellCallback {
@@ -704,59 +564,6 @@ public final class Pm {
}
}
- /**
- * Execute the list sub-command.
- *
- * pm list [package | packages]
- * pm list permission-groups
- * pm list permissions
- * pm list features
- * pm list libraries
- * pm list instrumentation
- */
- private int runList() {
- final String type = nextArg();
- if ("users".equals(type)) {
- return runShellCommand("user", new String[] { "list" });
- }
- return runShellCommand("package", mArgs);
- }
-
- private int runUninstall() {
- return runShellCommand("package", mArgs);
- }
-
- private int runPath() {
- int userId = UserHandle.USER_SYSTEM;
- String option = nextOption();
- if (option != null && option.equals("--user")) {
- String optionData = nextOptionData();
- if (optionData == null || !isNumber(optionData)) {
- System.err.println("Error: no USER_ID specified");
- return showUsage();
- } else {
- userId = Integer.parseInt(optionData);
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified");
- return 1;
- }
- return displayPackageFilePath(pkg, userId);
- }
-
- private int runDump() {
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified");
- return 1;
- }
- ActivityManager.dumpPackageStateStatic(FileDescriptor.out, pkg);
- return 0;
- }
-
class LocalPackageInstallObserver extends PackageInstallObserver {
boolean finished;
int result;
@@ -779,403 +586,16 @@ public final class Pm {
}
}
- // pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
- private int runSetAppLink() {
- int userId = UserHandle.USER_SYSTEM;
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- if (userId < 0) {
- System.err.println("Error: user must be >= 0");
- return 1;
- }
- } else {
- System.err.println("Error: unknown option: " + opt);
- return showUsage();
- }
- }
-
- // Package name to act on; required
- final String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified.");
- return showUsage();
- }
-
- // State to apply; {always|ask|never|undefined}, required
- final String modeString = nextArg();
- if (modeString == null) {
- System.err.println("Error: no app link state specified.");
- return showUsage();
- }
-
- final int newMode;
- switch (modeString.toLowerCase()) {
- case "undefined":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
- break;
-
- case "always":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
- break;
-
- case "ask":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
- break;
-
- case "always-ask":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
- break;
-
- case "never":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
- break;
-
- default:
- System.err.println("Error: unknown app link state '" + modeString + "'");
- return 1;
- }
-
- try {
- final PackageInfo info = mPm.getPackageInfo(pkg, 0, userId);
- if (info == null) {
- System.err.println("Error: package " + pkg + " not found.");
- return 1;
- }
-
- if ((info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
- System.err.println("Error: package " + pkg + " does not handle web links.");
- return 1;
- }
-
- if (!mPm.updateIntentVerificationStatus(pkg, newMode, userId)) {
- System.err.println("Error: unable to update app link status for " + pkg);
- return 1;
- }
- } catch (Exception e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
-
- return 0;
- }
-
- // pm get-app-link [--user USER_ID] PACKAGE
- private int runGetAppLink() {
- int userId = UserHandle.USER_SYSTEM;
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- if (userId < 0) {
- System.err.println("Error: user must be >= 0");
- return 1;
- }
- } else {
- System.err.println("Error: unknown option: " + opt);
- return showUsage();
- }
- }
-
- // Package name to act on; required
- final String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified.");
- return showUsage();
- }
-
- try {
- final PackageInfo info = mPm.getPackageInfo(pkg, 0, userId);
- if (info == null) {
- System.err.println("Error: package " + pkg + " not found.");
- return 1;
- }
-
- if ((info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
- System.err.println("Error: package " + pkg + " does not handle web links.");
- return 1;
- }
-
- System.out.println(linkStateToString(mPm.getIntentVerificationStatus(pkg, userId)));
- } catch (Exception e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
-
- return 0;
- }
-
- private String linkStateToString(int state) {
- switch (state) {
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: return "undefined";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
- }
- return "Unknown link state: " + state;
- }
-
- private int runSetInstallLocation() {
- int loc;
-
- String arg = nextArg();
- if (arg == null) {
- System.err.println("Error: no install location specified.");
- return 1;
- }
- try {
- loc = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- System.err.println("Error: install location has to be a number.");
- return 1;
- }
- try {
- if (!mPm.setInstallLocation(loc)) {
- System.err.println("Error: install location has to be a number.");
- return 1;
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private int runGetInstallLocation() {
- try {
- int loc = mPm.getInstallLocation();
- String locStr = "invalid";
- if (loc == PackageHelper.APP_INSTALL_AUTO) {
- locStr = "auto";
- } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) {
- locStr = "internal";
- } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
- locStr = "external";
- }
- System.out.println(loc + "[" + locStr + "]");
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private int runSetInstaller() throws RemoteException {
- final String targetPackage = nextArg();
- final String installerPackageName = nextArg();
-
- if (targetPackage == null || installerPackageName == null) {
- throw new IllegalArgumentException(
- "must provide both target and installer package names");
- }
-
- mPm.setInstallerPackageName(targetPackage, installerPackageName);
- System.out.println("Success");
- return 0;
- }
-
- public int runCreateUser() {
- String name;
- int userId = -1;
- int flags = 0;
- String opt;
- while ((opt = nextOption()) != null) {
- if ("--profileOf".equals(opt)) {
- String optionData = nextOptionData();
- if (optionData == null || !isNumber(optionData)) {
- System.err.println("Error: no USER_ID specified");
- return showUsage();
- } else {
- userId = Integer.parseInt(optionData);
- }
- } else if ("--managed".equals(opt)) {
- flags |= UserInfo.FLAG_MANAGED_PROFILE;
- } else if ("--restricted".equals(opt)) {
- flags |= UserInfo.FLAG_RESTRICTED;
- } else if ("--ephemeral".equals(opt)) {
- flags |= UserInfo.FLAG_EPHEMERAL;
- } else if ("--guest".equals(opt)) {
- flags |= UserInfo.FLAG_GUEST;
- } else if ("--demo".equals(opt)) {
- flags |= UserInfo.FLAG_DEMO;
- } else {
- System.err.println("Error: unknown option " + opt);
- return showUsage();
- }
- }
- String arg = nextArg();
- if (arg == null) {
- System.err.println("Error: no user name specified.");
- return 1;
- }
- name = arg;
- try {
- UserInfo info;
- if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
- // In non-split user mode, userId can only be SYSTEM
- int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
- info = mUm.createRestrictedProfile(name, parentUserId);
- mAm.addSharedAccountsFromParentUser(parentUserId, userId,
- (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
- } else if (userId < 0) {
- info = mUm.createUser(name, flags);
- } else {
- info = mUm.createProfileForUser(name, flags, userId, null);
- }
-
- if (info != null) {
- System.out.println("Success: created user id " + info.id);
- return 0;
- } else {
- System.err.println("Error: couldn't create User.");
- return 1;
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- public int runRemoveUser() {
- int userId;
- String arg = nextArg();
- if (arg == null) {
- System.err.println("Error: no user id specified.");
- return 1;
- }
- try {
- userId = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- System.err.println("Error: user id '" + arg + "' is not a number.");
- return 1;
- }
- try {
- if (mUm.removeUser(userId)) {
- System.out.println("Success: removed user");
- return 0;
- } else {
- System.err.println("Error: couldn't remove user id " + userId);
- return 1;
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- public int runGetMaxUsers() {
- System.out.println("Maximum supported users: " + UserManager.getMaxSupportedUsers());
- return 0;
- }
-
- public int runForceDexOpt() {
- final String packageName = nextArg();
- try {
- mPm.forceDexOpt(packageName);
- return 0;
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- public int runMovePackage() {
- final String packageName = nextArg();
- String volumeUuid = nextArg();
- if ("internal".equals(volumeUuid)) {
- volumeUuid = null;
- }
-
- try {
- final int moveId = mPm.movePackage(packageName, volumeUuid);
-
- int status = mPm.getMoveStatus(moveId);
- while (!PackageManager.isMoveStatusFinished(status)) {
- SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
- status = mPm.getMoveStatus(moveId);
- }
-
- if (status == PackageManager.MOVE_SUCCEEDED) {
- System.out.println("Success");
- return 0;
- } else {
- System.err.println("Failure [" + status + "]");
- return 1;
- }
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- public int runMovePrimaryStorage() {
- String volumeUuid = nextArg();
- if ("internal".equals(volumeUuid)) {
- volumeUuid = null;
- }
-
- try {
- final int moveId = mPm.movePrimaryStorage(volumeUuid);
-
- int status = mPm.getMoveStatus(moveId);
- while (!PackageManager.isMoveStatusFinished(status)) {
- SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
- status = mPm.getMoveStatus(moveId);
- }
-
- if (status == PackageManager.MOVE_SUCCEEDED) {
- System.out.println("Success");
- return 0;
- } else {
- System.err.println("Failure [" + status + "]");
- return 1;
- }
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- public int runSetUserRestriction() {
- int userId = UserHandle.USER_SYSTEM;
- String opt = nextOption();
- if (opt != null && "--user".equals(opt)) {
- String arg = nextArg();
- if (arg == null || !isNumber(arg)) {
- System.err.println("Error: valid userId not specified");
- return 1;
- }
- userId = Integer.parseInt(arg);
- }
-
- String restriction = nextArg();
- String arg = nextArg();
- boolean value;
- if ("1".equals(arg)) {
- value = true;
- } else if ("0".equals(arg)) {
- value = false;
- } else {
- System.err.println("Error: valid value not specified");
- return 1;
- }
+ private static boolean isNumber(String s) {
try {
- mUm.setUserRestriction(restriction, value, userId);
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- return 1;
+ Integer.parseInt(s);
+ } catch (NumberFormatException nfe) {
+ return false;
}
+ return true;
}
- static class ClearDataObserver extends IPackageDataObserver.Stub {
+ static class ClearCacheObserver extends IPackageDataObserver.Stub {
boolean finished;
boolean result;
@@ -1187,242 +607,10 @@ public final class Pm {
notifyAll();
}
}
- }
-
- private int runClear() {
- int userId = UserHandle.USER_SYSTEM;
- String option = nextOption();
- if (option != null && option.equals("--user")) {
- String optionData = nextOptionData();
- if (optionData == null || !isNumber(optionData)) {
- System.err.println("Error: no USER_ID specified");
- return showUsage();
- } else {
- userId = Integer.parseInt(optionData);
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified");
- return showUsage();
- }
-
- ClearDataObserver obs = new ClearDataObserver();
- try {
- ActivityManager.getService().clearApplicationUserData(pkg, obs, userId);
- synchronized (obs) {
- while (!obs.finished) {
- try {
- obs.wait();
- } catch (InterruptedException e) {
- }
- }
- }
-
- if (obs.result) {
- System.out.println("Success");
- return 0;
- } else {
- System.err.println("Failed");
- return 1;
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private static String enabledSettingToString(int state) {
- switch (state) {
- case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
- return "default";
- case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
- return "enabled";
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
- return "disabled";
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
- return "disabled-user";
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
- return "disabled-until-used";
- }
- return "unknown";
- }
-
- private static boolean isNumber(String s) {
- try {
- Integer.parseInt(s);
- } catch (NumberFormatException nfe) {
- return false;
- }
- return true;
- }
-
- private int runSetEnabledSetting(int state) {
- int userId = UserHandle.USER_SYSTEM;
- String option = nextOption();
- if (option != null && option.equals("--user")) {
- String optionData = nextOptionData();
- if (optionData == null || !isNumber(optionData)) {
- System.err.println("Error: no USER_ID specified");
- return showUsage();
- } else {
- userId = Integer.parseInt(optionData);
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package or component specified");
- return showUsage();
- }
- ComponentName cn = ComponentName.unflattenFromString(pkg);
- if (cn == null) {
- try {
- mPm.setApplicationEnabledSetting(pkg, state, 0, userId,
- "shell:" + android.os.Process.myUid());
- System.out.println("Package " + pkg + " new state: "
- + enabledSettingToString(
- mPm.getApplicationEnabledSetting(pkg, userId)));
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- } else {
- try {
- mPm.setComponentEnabledSetting(cn, state, 0, userId);
- System.out.println("Component " + cn.toShortString() + " new state: "
- + enabledSettingToString(
- mPm.getComponentEnabledSetting(cn, userId)));
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
- }
-
- private int runSetHiddenSetting(boolean state) {
- int userId = UserHandle.USER_SYSTEM;
- String option = nextOption();
- if (option != null && option.equals("--user")) {
- String optionData = nextOptionData();
- if (optionData == null || !isNumber(optionData)) {
- System.err.println("Error: no USER_ID specified");
- return showUsage();
- } else {
- userId = Integer.parseInt(optionData);
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package or component specified");
- return showUsage();
- }
- try {
- mPm.setApplicationHiddenSettingAsUser(pkg, state, userId);
- System.out.println("Package " + pkg + " new hidden state: "
- + mPm.getApplicationHiddenSettingAsUser(pkg, userId));
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- private int runGrantRevokePermission(boolean grant) {
- int userId = UserHandle.USER_SYSTEM;
-
- String opt = null;
- while ((opt = nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = Integer.parseInt(nextArg());
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified");
- return showUsage();
- }
- String perm = nextArg();
- if (perm == null) {
- System.err.println("Error: no permission specified");
- return showUsage();
- }
-
- try {
- if (grant) {
- mPm.grantRuntimePermission(pkg, perm, userId);
- } else {
- mPm.revokeRuntimePermission(pkg, perm, userId);
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- } catch (IllegalArgumentException e) {
- System.err.println("Bad argument: " + e.toString());
- return showUsage();
- } catch (SecurityException e) {
- System.err.println("Operation not allowed: " + e.toString());
- return 1;
- }
- }
-
- private int runResetPermissions() {
- try {
- mPm.resetRuntimePermissions();
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- } catch (IllegalArgumentException e) {
- System.err.println("Bad argument: " + e.toString());
- return showUsage();
- } catch (SecurityException e) {
- System.err.println("Operation not allowed: " + e.toString());
- return 1;
- }
- }
- private int runSetPermissionEnforced() {
- final String permission = nextArg();
- if (permission == null) {
- System.err.println("Error: no permission specified");
- return showUsage();
- }
- final String enforcedRaw = nextArg();
- if (enforcedRaw == null) {
- System.err.println("Error: no enforcement specified");
- return showUsage();
- }
- final boolean enforced = Boolean.parseBoolean(enforcedRaw);
- try {
- mPm.setPermissionEnforced(permission, enforced);
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- } catch (IllegalArgumentException e) {
- System.err.println("Bad argument: " + e.toString());
- return showUsage();
- } catch (SecurityException e) {
- System.err.println("Operation not allowed: " + e.toString());
- return 1;
- }
}
- static class ClearCacheObserver extends IPackageDataObserver.Stub {
+ static class ClearDataObserver extends IPackageDataObserver.Stub {
boolean finished;
boolean result;
@@ -1434,66 +622,6 @@ public final class Pm {
notifyAll();
}
}
-
- }
-
- private int runTrimCaches() {
- String size = nextArg();
- if (size == null) {
- System.err.println("Error: no size specified");
- return showUsage();
- }
- long multiplier = 1;
- int len = size.length();
- char c = size.charAt(len - 1);
- if (c < '0' || c > '9') {
- if (c == 'K' || c == 'k') {
- multiplier = 1024L;
- } else if (c == 'M' || c == 'm') {
- multiplier = 1024L*1024L;
- } else if (c == 'G' || c == 'g') {
- multiplier = 1024L*1024L*1024L;
- } else {
- System.err.println("Invalid suffix: " + c);
- return showUsage();
- }
- size = size.substring(0, len-1);
- }
- long sizeVal;
- try {
- sizeVal = Long.parseLong(size) * multiplier;
- } catch (NumberFormatException e) {
- System.err.println("Error: expected number at: " + size);
- return showUsage();
- }
- String volumeUuid = nextArg();
- if ("internal".equals(volumeUuid)) {
- volumeUuid = null;
- }
- ClearDataObserver obs = new ClearDataObserver();
- try {
- mPm.freeStorageAndNotify(volumeUuid, sizeVal,
- StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED, obs);
- synchronized (obs) {
- while (!obs.finished) {
- try {
- obs.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- return 0;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- } catch (IllegalArgumentException e) {
- System.err.println("Bad argument: " + e.toString());
- return showUsage();
- } catch (SecurityException e) {
- System.err.println("Operation not allowed: " + e.toString());
- return 1;
- }
}
/**
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 8505d4c47aba..db634d4a6601 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -15,6 +15,7 @@
LOCAL_PATH:= $(call my-dir)
statsd_common_src := \
+ ../../core/java/android/os/IStatsCallbacks.aidl \
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
src/stats_log.proto \
@@ -37,6 +38,8 @@ statsd_common_src := \
src/matchers/matcher_util.cpp \
src/matchers/SimpleLogMatchingTracker.cpp \
src/metrics/CountAnomalyTracker.cpp \
+ src/metrics/MetricProducer.cpp \
+ src/metrics/EventMetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
src/metrics/DurationMetricProducer.cpp \
src/metrics/MetricsManager.cpp \
@@ -64,7 +67,8 @@ statsd_common_shared_libraries := \
libselinux \
libutils \
libservices \
- libandroidfw
+ libandroidfw \
+ libprotoutil
# =========
# statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index e7825cf75159..68f48a4f4c37 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -33,8 +33,9 @@ namespace android {
namespace os {
namespace statsd {
-StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap)
- : m_dropbox_writer("all-logs"), mUidMap(uidMap) {
+StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
+ const std::function<void(const vector<uint8_t>&)>& pushLog)
+ : m_dropbox_writer("all-logs"), mUidMap(uidMap), mPushLog(pushLog) {
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -91,6 +92,41 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
}
}
+void StatsLogProcessor::addEventMetricData(const EventMetricData& eventMetricData) {
+ // TODO: Replace this code when MetricsManager.onDumpReport() is ready to
+ // get a list of byte arrays.
+ flushIfNecessary(eventMetricData);
+ const int numBytes = eventMetricData.ByteSize();
+ char buffer[numBytes];
+ eventMetricData.SerializeToArray(&buffer[0], numBytes);
+ string bufferString(buffer, numBytes);
+ mEvents.push_back(bufferString);
+ mBufferSize += eventMetricData.ByteSize();
+}
+
+void StatsLogProcessor::flushIfNecessary(const EventMetricData& eventMetricData) {
+ if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) {
+ flush();
+ }
+}
+
+void StatsLogProcessor::flush() {
+ StatsLogReport logReport;
+ for (string eventBuffer : mEvents) {
+ EventMetricData eventFromBuffer;
+ eventFromBuffer.ParseFromString(eventBuffer);
+ EventMetricData* newEntry = logReport.mutable_event_metrics()->add_data();
+ newEntry->CopyFrom(eventFromBuffer);
+ }
+
+ const int numBytes = logReport.ByteSize();
+ vector<uint8_t> logReportBuffer(numBytes);
+ logReport.SerializeToArray(&logReportBuffer[0], numBytes);
+ mPushLog(logReportBuffer);
+ mEvents.clear();
+ mBufferSize = 0;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 3cefd29fa026..08090c11f724 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -33,7 +33,8 @@ namespace statsd {
class StatsLogProcessor : public ConfigListener {
public:
- StatsLogProcessor(const sp<UidMap>& uidMap);
+ StatsLogProcessor(const sp<UidMap>& uidMap,
+ const std::function<void(const vector<uint8_t>&)>& pushLog);
virtual ~StatsLogProcessor();
virtual void OnLogEvent(const LogEvent& event);
@@ -44,6 +45,9 @@ public:
// TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
std::vector<StatsLogReport> onDumpReport(const ConfigKey& key);
+ /* Request a flush through a binder call. */
+ void flush();
+
private:
// TODO: use EventMetrics to log the events.
DropboxWriter m_dropbox_writer;
@@ -51,6 +55,33 @@ private:
std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;
sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
+
+ /* Max *serialized* size of the logs kept in memory before flushing through binder call.
+ Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
+ So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
+ is higher than its serialized size.
+ */
+ static const size_t kMaxSerializedBytes = 16 * 1024;
+
+ /* List of data that was captured for a single metric over a given interval of time. */
+ vector<string> mEvents;
+
+ /* Current *serialized* size of the logs kept in memory.
+ To save computation, we will not calculate the size of the StatsLogReport every time when a
+ new entry is added, which would recursively call ByteSize() on every log entry. Instead, we
+ keep the sum of all individual stats log entry sizes. The size of a proto is approximately
+ the sum of the size of all member protos.
+ */
+ size_t mBufferSize = 0;
+
+ /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
+ the logs to dropbox if true. */
+ void flushIfNecessary(const EventMetricData& eventMetricData);
+
+ /* Append event metric data to StatsLogReport. */
+ void addEventMetricData(const EventMetricData& eventMetricData);
+
+ std::function<void(const vector<uint8_t>&)> mPushLog;
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1faeee0a6554..604753ef54a0 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -68,7 +68,9 @@ StatsService::StatsService(const sp<Looper>& handlerLooper)
mStatsPullerManager = new StatsPullerManager();
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(mUidMap);
+ mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) {
+ pushLog(log);
+ });
mConfigManager->AddListener(mProcessor);
@@ -507,6 +509,40 @@ void StatsService::OnLogEvent(const LogEvent& event) {
mProcessor->OnLogEvent(event);
}
+Status StatsService::requestPush() {
+ mProcessor->flush();
+ return Status::ok();
+}
+
+Status StatsService::pushLog(const vector<uint8_t>& log) {
+ std::lock_guard<std::mutex> lock(mLock);
+ for (size_t i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks[i]->onReceiveLogs((vector<uint8_t>*)&log);
+ }
+ return Status::ok();
+}
+
+Status StatsService::subscribeStatsLog(const sp<IStatsCallbacks>& callback) {
+ std::lock_guard<std::mutex> lock(mLock);
+ for (size_t i = 0; i < mCallbacks.size(); i++) {
+ if (mCallbacks[i] == callback) {
+ return Status::fromStatusT(-errno);
+ }
+ }
+ mCallbacks.add(callback);
+ IInterface::asBinder(callback)->linkToDeath(this);
+ return Status::ok();
+}
+
+void StatsService::binderDied(const wp<IBinder>& who) {
+ for (size_t i = 0; i < mCallbacks.size(); i++) {
+ if (IInterface::asBinder(mCallbacks[i]) == who) {
+ mCallbacks.removeAt(i);
+ break;
+ }
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 449a2b84bfd7..7f046584b2d0 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -24,6 +24,7 @@
#include "packages/UidMap.h"
#include <android/os/BnStatsManager.h>
+#include <android/os/IStatsCallbacks.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
#include <binder/IShellCallback.h>
@@ -42,7 +43,7 @@ namespace android {
namespace os {
namespace statsd {
-class StatsService : public BnStatsManager, public LogListener {
+class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient {
public:
StatsService(const sp<Looper>& handlerLooper);
virtual ~StatsService();
@@ -70,6 +71,22 @@ public:
*/
virtual void OnLogEvent(const LogEvent& event);
+ /**
+ * Binder call to force trigger pushLog. This would be called by callback
+ * clients.
+ */
+ virtual Status requestPush() override;
+
+ /**
+ * Pushes stats log entries from statsd to callback clients.
+ */
+ Status pushLog(const vector<uint8_t>& log);
+
+ /**
+ * Binder call to listen to statsd to send stats log entries.
+ */
+ virtual Status subscribeStatsLog(const sp<IStatsCallbacks>& callbacks) override;
+
// TODO: public for testing since statsd doesn't run when system starts. Change to private
// later.
/** Inform statsCompanion that statsd is ready. */
@@ -78,6 +95,9 @@ public:
/** Fetches and returns the StatsCompanionService. */
static sp<IStatsCompanionService> getStatsCompanionService();
+ /** IBinder::DeathRecipient */
+ virtual void binderDied(const wp<IBinder>& who) override;
+
private:
/**
* Load system properties at init.
@@ -159,6 +179,16 @@ private:
* Whether this is an eng build.
*/
bool mEngBuild;
+
+ /**
+ * Lock for callback handling.
+ */
+ std::mutex mLock;
+
+ /**
+ * Vector maintaining the list of callbacks for clients.
+ */
+ Vector< sp<IStatsCallbacks> > mCallbacks;
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index bde3846ab66b..aff476814c94 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "SimpleConditionTracker.h"
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 038edd313081..c16971aeb8a8 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -120,7 +120,7 @@ static StatsdConfig build_fake_config() {
int WAKE_LOCK_TAG_ID = 11;
int WAKE_LOCK_UID_KEY_ID = 1;
- int WAKE_LOCK_STATE_KEY = 2;
+ int WAKE_LOCK_STATE_KEY = 3;
int WAKE_LOCK_ACQUIRE_VALUE = 1;
int WAKE_LOCK_RELEASE_VALUE = 0;
@@ -130,12 +130,12 @@ static StatsdConfig build_fake_config() {
int APP_USAGE_FOREGROUND = 1;
int APP_USAGE_BACKGROUND = 0;
- int SCREEN_EVENT_TAG_ID = 2;
+ int SCREEN_EVENT_TAG_ID = 29;
int SCREEN_EVENT_STATE_KEY = 1;
int SCREEN_EVENT_ON_VALUE = 2;
int SCREEN_EVENT_OFF_VALUE = 1;
- int UID_PROCESS_STATE_TAG_ID = 3;
+ int UID_PROCESS_STATE_TAG_ID = 27;
int UID_PROCESS_STATE_UID_KEY = 1;
// Count Screen ON events.
@@ -144,6 +144,12 @@ static StatsdConfig build_fake_config() {
metric->set_what("SCREEN_TURNED_ON");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ // Anomaly threshold for screen-on count.
+ Alert* alert = metric->add_alerts();
+ alert->set_number_of_buckets(6);
+ alert->set_trigger_if_sum_gt(10);
+ alert->set_refractory_period_secs(30);
+
// Count process state changes, slice by uid.
metric = config.add_count_metric();
metric->set_metric_id(2);
@@ -161,7 +167,7 @@ static StatsdConfig build_fake_config() {
keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
metric->set_condition("SCREEN_IS_OFF");
- // Count wake lock, slice by uid, while SCREEN_IS_OFF and app in background
+ // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
metric = config.add_count_metric();
metric->set_metric_id(4);
metric->set_what("APP_GET_WL");
@@ -189,6 +195,11 @@ static StatsdConfig build_fake_config() {
link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+ // Add an EventMetric to log process state change events.
+ EventMetric* eventMetric = config.add_event_metric();
+ eventMetric->set_metric_id(6);
+ eventMetric->set_what("SCREEN_TURNED_ON");
+
// Event matchers............
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_TURNED_ON");
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index fb992c11aad7..1a039f6d61c5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -17,6 +17,7 @@
#include "logd/LogEvent.h"
#include <sstream>
+#include "stats_util.h"
namespace android {
namespace os {
@@ -24,6 +25,7 @@ namespace statsd {
using std::ostringstream;
using std::string;
+using android::util::ProtoOutputStream;
// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
// for strings is not cleared before we can read them.
@@ -203,30 +205,24 @@ string LogEvent::ToString() const {
return result.str();
}
-void LogEvent::ToProto(EventMetricData* out) const {
- // TODO: Implement this when we have the ProtoOutputStream version.
-
- // set timestamp of the event.
- out->set_timestamp_nanos(mTimestampNs);
-
- // uint64_t token = proto->StartObject(EventMetricData.FIELD);
+void LogEvent::ToProto(ProtoOutputStream& proto) const {
+ long long atomToken = proto.start(TYPE_MESSAGE + mTagId);
const size_t N = mElements.size();
for (size_t i=0; i<N; i++) {
const int key = i + 1;
const android_log_list_element& elem = mElements[i];
if (elem.type == EVENT_TYPE_INT) {
- // proto->Write(key, elem.data.int32);
+ proto.write(TYPE_INT32 + key, elem.data.int32);
} else if (elem.type == EVENT_TYPE_LONG) {
- // proto->Write(key, elem.data.int64);
+ proto.write(TYPE_INT64 + key, (long long)elem.data.int64);
} else if (elem.type == EVENT_TYPE_FLOAT) {
- // proto->Write(key, elem.data.float32);
+ proto.write(TYPE_FLOAT + key, elem.data.float32);
} else if (elem.type == EVENT_TYPE_STRING) {
- // proto->Write(key, elem.data.string);
+ proto.write(TYPE_STRING + key, elem.data.string);
}
}
-
- //proto->EndObject(token);
+ proto.end(atomToken);
}
} // namespace statsd
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 410267507430..9ef20ea6968c 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,9 +18,10 @@
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-#include <utils/Errors.h>
+#include <android/util/ProtoOutputStream.h>
#include <log/log_event_list.h>
#include <log/log_read.h>
+#include <utils/Errors.h>
#include <memory>
#include <string>
@@ -80,10 +81,9 @@ public:
string ToString() const;
/**
- * Write this object as an EventMetricData proto object.
- * TODO: Use the streaming output generator to do this instead of this proto lite object?
+ * Write this object to a ProtoOutputStream.
*/
- void ToProto(EventMetricData* out) const;
+ void ToProto(android::util::ProtoOutputStream& out) const;
/*
* Get a KeyValuePair proto object.
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
index e1c2b8b3a90e..7aa748fdeb77 100644
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
+++ b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
@@ -17,23 +17,22 @@
#define DEBUG true // STOPSHIP if true
#include "Log.h"
-#define VLOG(...) \
- if (DEBUG) ALOGD(__VA_ARGS__);
-
#include "CountAnomalyTracker.h"
+#include <time.h>
+
namespace android {
namespace os {
namespace statsd {
-CountAnomalyTracker::CountAnomalyTracker(size_t numBuckets, int thresholdGt)
- : mNumPastBuckets(numBuckets > 0 ? numBuckets - 1 : 0),
- mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr),
- mThresholdGt(thresholdGt) {
+CountAnomalyTracker::CountAnomalyTracker(const Alert& alert)
+ : mAlert(alert),
+ mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0),
+ mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) {
VLOG("CountAnomalyTracker() called");
- if (numBuckets < 1) {
- ALOGE("Cannot create CountAnomalyTracker with %zu buckets", numBuckets);
+ if (alert.number_of_buckets() < 1) {
+ ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets());
}
reset(); // initialization
}
@@ -84,22 +83,45 @@ void CountAnomalyTracker::reset() {
}
void CountAnomalyTracker::checkAnomaly(int currentCount) {
- // Note that this works even if mNumPastBuckets < 1 (since then
- // mSumPastCounters = 0 so the comparison is based only on currentCount).
+ // Skip the check if in refractory period.
+ if (time(nullptr) < mRefractoryPeriodEndsSec) {
+ VLOG("Skipping anomaly check since within refractory period");
+ return;
+ }
// TODO: Remove these extremely verbose debugging log.
- VLOG("Checking whether %d + %d > %d",
- mSumPastCounters, currentCount, mThresholdGt);
+ VLOG("Checking whether %d + %d > %lld",
+ mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt());
- if (mSumPastCounters + currentCount > mThresholdGt) {
+ // Note that this works even if mNumPastBuckets < 1 (since then
+ // mSumPastCounters = 0 so the comparison is based only on currentCount).
+ if (mAlert.has_trigger_if_sum_gt() &&
+ mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) {
declareAnomaly();
}
}
void CountAnomalyTracker::declareAnomaly() {
- // TODO: check that not in refractory period.
- // TODO: Do something.
- ALOGI("An anomaly has occurred!");
+ // TODO(guardrail): Consider guarding against too short refractory periods.
+ time_t currTime = time(nullptr);
+ mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs();
+
+ // TODO: If we had access to the bucket_size_millis, consider calling reset()
+ // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000).
+
+ if (mAlert.has_incidentd_details()) {
+ const Alert_IncidentdDetails& incident = mAlert.incidentd_details();
+ if (incident.has_alert_name()) {
+ ALOGW("An anomaly (%s) has occurred! Informing incidentd.",
+ incident.alert_name().c_str());
+ } else {
+ // TODO: Can construct a name based on the criteria (and/or relay the criteria).
+ ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
+ }
+ // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
+ } else {
+ ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
+ }
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h
index 449dee90e0a6..79c47d2a6bab 100644
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.h
+++ b/cmds/statsd/src/metrics/CountAnomalyTracker.h
@@ -17,16 +17,20 @@
#ifndef COUNT_ANOMALY_TRACKER_H
#define COUNT_ANOMALY_TRACKER_H
-#include <stdlib.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+
#include <memory> // unique_ptr
+#include <stdlib.h>
namespace android {
namespace os {
namespace statsd {
+// TODO: Can probably be used for Count, Value, and Gauge. If so, rename to ValueAnomalyTracker.
+// (caveat: currently, the value cannot be negative. Probably fine for P.)
class CountAnomalyTracker {
public:
- CountAnomalyTracker(size_t numBuckets, int thresholdGt);
+ CountAnomalyTracker(const Alert& alert);
virtual ~CountAnomalyTracker();
@@ -43,6 +47,9 @@ public:
void checkAnomaly(int currentCount);
private:
+ // statsd_config.proto Alert message that defines this tracker.
+ const Alert mAlert;
+
// Number of past buckets. One less than the total number of buckets needed
// for the anomaly detection (since the current bucket is not in the past).
const size_t mNumPastBuckets;
@@ -59,8 +66,9 @@ private:
// Index of the oldest bucket (i.e. the next bucket to be overwritten).
size_t mOldestBucketIndex = 0;
- // If mSumPastCounters + currentCount > mThresholdGt --> Anomaly!
- const int mThresholdGt;
+ // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds.
+ // If an anomaly was never declared, set to 0.
+ time_t mRefractoryPeriodEndsSec = 0;
void declareAnomaly();
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 1f07914175d2..7bb9c8a502c1 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -39,9 +39,7 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co
const sp<ConditionWizard>& wizard)
// TODO: Pass in the start time from MetricsManager, instead of calling time() here.
: MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
- mMetric(metric),
- // TODO: read mAnomalyTracker parameters from config file.
- mAnomalyTracker(6, 10) {
+ mMetric(metric) {
// TODO: evaluate initial conditions. and set mConditionMet.
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -49,6 +47,17 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co
mBucketSizeNs = LLONG_MAX;
}
+ mAnomalyTrackers.reserve(metric.alerts_size());
+ for (int i = 0; i < metric.alerts_size(); i++) {
+ const Alert& alert = metric.alerts(i);
+ if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
+ mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert));
+ } else {
+ ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
+ alert.trigger_if_sum_gt(), alert.number_of_buckets());
+ }
+ }
+
// TODO: use UidMap if uid->pkg_name is required
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
@@ -124,52 +133,23 @@ void CountMetricProducer::onConditionChanged(const bool conditionMet) {
mCondition = conditionMet;
}
-void CountMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+void CountMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
- // this is old event, maybe statsd restarted?
- if (eventTimeNs < mStartTimeNs) {
- return;
- }
flushCounterIfNeeded(eventTimeNs);
- if (mConditionSliced) {
- map<string, HashableDimensionKey> conditionKeys;
- for (const auto& link : mConditionLinks) {
- VLOG("Condition link key_in_main size %d", link.key_in_main_size());
- HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
- conditionKeys[link.condition()] = conditionKey;
- }
- if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
- VLOG("metric %lld sliced condition not met", mMetric.metric_id());
- return;
- }
- } else {
- if (!mCondition) {
- VLOG("metric %lld condition not met", mMetric.metric_id());
- return;
- }
- }
-
- HashableDimensionKey hashableKey;
-
- if (mDimension.size() > 0) {
- vector<KeyValuePair> key = getDimensionKey(event, mDimension);
- hashableKey = getHashableKey(key);
- // Add the HashableDimensionKey->vector<KeyValuePair> to the map, because StatsLogReport
- // expects vector<KeyValuePair>.
- if (mDimensionKeyMap.find(hashableKey) == mDimensionKeyMap.end()) {
- mDimensionKeyMap[hashableKey] = key;
- }
- } else {
- hashableKey = DEFAULT_DIMENSION_KEY;
+ if (condition == false) {
+ return;
}
- auto it = mCurrentSlicedCounter.find(hashableKey);
+ auto it = mCurrentSlicedCounter.find(eventKey);
if (it == mCurrentSlicedCounter.end()) {
// create a counter for the new key
- mCurrentSlicedCounter[hashableKey] = 1;
+ mCurrentSlicedCounter[eventKey] = 1;
} else {
// increment the existing value
@@ -177,8 +157,13 @@ void CountMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const Log
count++;
}
- VLOG("metric %lld %s->%d", mMetric.metric_id(), hashableKey.c_str(),
- mCurrentSlicedCounter[hashableKey]);
+ // TODO: Re-add anomaly detection (similar to):
+ // for (auto& tracker : mAnomalyTrackers) {
+ // tracker->checkAnomaly(mCounter);
+ // }
+
+ VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
+ mCurrentSlicedCounter[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
@@ -205,6 +190,11 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
counter.second);
}
+ // TODO: Re-add anomaly detection (similar to):
+ // for (auto& tracker : mAnomalyTrackers) {
+ // tracker->addPastBucket(mCounter, numBucketsForward);
+ //}
+
// Reset counters
mCurrentSlicedCounter.clear();
@@ -215,4 +205,4 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
} // namespace statsd
} // namespace os
-} // namespace android
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index f0d60255af51..340c8309b7fa 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -41,8 +41,6 @@ public:
virtual ~CountMetricProducer();
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) override;
-
void onConditionChanged(const bool conditionMet) override;
void finish() override;
@@ -54,17 +52,22 @@ public:
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+protected:
+ void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey,
+ bool condition, const LogEvent& event) override;
+
private:
const CountMetric mMetric;
- CountAnomalyTracker mAnomalyTracker;
-
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<HashableDimensionKey, std::vector<CountBucketInfo>> mPastBuckets;
// The current bucket.
std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter;
+ vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers;
+
void flushCounterIfNeeded(const uint64_t newEventTime);
};
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index aa597f421cb6..38e55fde40d5 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -137,11 +137,10 @@ StatsLogReport DurationMetricProducer::onDumpReport() {
return report;
};
-void DurationMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
- if (event.GetTimestampNs() < mStartTimeNs) {
- return;
- }
-
+void DurationMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKeys, bool condition,
+ const LogEvent& event) {
flushDurationIfNeeded(event.GetTimestampNs());
if (matcherIndex == mStopAllIndex) {
@@ -149,49 +148,20 @@ void DurationMetricProducer::onMatchedLogEvent(const size_t matcherIndex, const
return;
}
- HashableDimensionKey hashableKey;
- if (mDimension.size() > 0) {
- // hook up sliced counter with AnomalyMonitor.
- vector<KeyValuePair> key = getDimensionKey(event, mDimension);
- hashableKey = getHashableKey(key);
- // Add the HashableDimensionKey->DimensionKey to the map, because StatsLogReport expects
- // vector<KeyValuePair>.
- if (mDimensionKeyMap.find(hashableKey) == mDimensionKeyMap.end()) {
- mDimensionKeyMap[hashableKey] = key;
- }
- } else {
- hashableKey = DEFAULT_DIMENSION_KEY;
- }
-
- if (mCurrentSlicedDuration.find(hashableKey) == mCurrentSlicedDuration.end() &&
- mConditionSliced) {
+ if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end() && mConditionSliced) {
// add the durationInfo for the current bucket.
- auto& durationInfo = mCurrentSlicedDuration[hashableKey];
- auto& conditionKeys = durationInfo.conditionKeys;
- // get and cache the keys for query condition.
- for (const auto& link : mConditionLinks) {
- HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
- conditionKeys[link.condition()] = conditionKey;
- }
- }
-
- bool conditionMet;
- if (mConditionSliced) {
- const auto& conditionKeys = mCurrentSlicedDuration[hashableKey].conditionKeys;
- conditionMet =
- mWizard->query(mConditionTrackerIndex, conditionKeys) == ConditionState::kTrue;
- } else {
- conditionMet = mCondition;
+ auto& durationInfo = mCurrentSlicedDuration[eventKey];
+ durationInfo.conditionKeys = conditionKeys;
}
if (matcherIndex == mStartIndex) {
- VLOG("Metric %lld Key: %s Start, Condition %d", mMetric.metric_id(), hashableKey.c_str(),
- conditionMet);
- noteStart(hashableKey, conditionMet, event.GetTimestampNs());
+ VLOG("Metric %lld Key: %s Start, Condition %d", mMetric.metric_id(), eventKey.c_str(),
+ condition);
+ noteStart(eventKey, condition, event.GetTimestampNs());
} else if (matcherIndex == mStopIndex) {
- VLOG("Metric %lld Key: %s Stop, Condition %d", mMetric.metric_id(), hashableKey.c_str(),
- conditionMet);
- noteStop(hashableKey, event.GetTimestampNs());
+ VLOG("Metric %lld Key: %s Stop, Condition %d", mMetric.metric_id(), eventKey.c_str(),
+ condition);
+ noteStop(eventKey, event.GetTimestampNs());
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 44c325417bd7..19e2437ca538 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -61,8 +61,6 @@ public:
virtual ~DurationMetricProducer();
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) override;
-
void onConditionChanged(const bool conditionMet) override;
void finish() override;
@@ -74,6 +72,11 @@ public:
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+protected:
+ void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKeys,
+ bool condition, const LogEvent& event) override;
+
private:
const DurationMetric mMetric;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
new file mode 100644
index 000000000000..8b3f405eec80
--- /dev/null
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "EventMetricProducer.h"
+#include "stats_util.h"
+
+#include <cutils/log.h>
+#include <limits.h>
+#include <stdlib.h>
+
+using android::util::ProtoOutputStream;
+using std::map;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// for StatsLogReport
+const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_START_REPORT_NANOS = 2;
+const int FIELD_ID_END_REPORT_NANOS = 2;
+const int FIELD_ID_EVENT_METRICS = 4;
+// for EventMetricData
+const int FIELD_ID_TIMESTAMP_NANOS = 1;
+const int FIELD_ID_STATS_EVENTS = 2;
+// for CountMetricDataWrapper
+const int FIELD_ID_DATA = 1;
+
+EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard)
+ // TODO: Pass in the start time from MetricsManager, instead of calling time() here.
+ : MetricProducer((time(nullptr) * NANO_SECONDS_IN_A_SECOND), conditionIndex, wizard),
+ mMetric(metric) {
+ if (metric.links().size() > 0) {
+ mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
+ metric.links().end());
+ mConditionSliced = true;
+ }
+
+ startNewProtoOutputStream(mStartTimeNs);
+
+ VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ (long long)mBucketSizeNs, (long long)mStartTimeNs);
+}
+
+EventMetricProducer::~EventMetricProducer() {
+ VLOG("~EventMetricProducer() called");
+}
+
+void EventMetricProducer::startNewProtoOutputStream(long long startTime) {
+ mProto = std::make_unique<ProtoOutputStream>();
+ // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData,
+ // and StatsEvent.
+ mProto->write(TYPE_INT32 + FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(TYPE_INT64 + FIELD_ID_START_REPORT_NANOS, startTime);
+ mProtoToken = mProto->start(TYPE_MESSAGE + FIELD_ID_EVENT_METRICS);
+}
+
+void EventMetricProducer::finish() {
+}
+
+void EventMetricProducer::onSlicedConditionMayChange() {
+}
+
+StatsLogReport EventMetricProducer::onDumpReport() {
+ long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND;
+ mProto->end(mProtoToken);
+ mProto->write(TYPE_INT64 + FIELD_ID_END_REPORT_NANOS, endTime);
+
+ size_t bufferSize = mProto->size();
+ VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize);
+ std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
+ size_t pos = 0;
+ auto it = mProto->data();
+ while (it.readBuffer() != NULL) {
+ size_t toRead = it.currentToRead();
+ std::memcpy(&buffer[pos], it.readBuffer(), toRead);
+ pos += toRead;
+ it.rp()->move(toRead);
+ }
+
+ startNewProtoOutputStream(endTime);
+
+ // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
+ // return std::move(buffer);
+ return StatsLogReport();
+}
+
+void EventMetricProducer::onConditionChanged(const bool conditionMet) {
+ VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ mCondition = conditionMet;
+}
+
+void EventMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event) {
+
+ if (!condition) {
+ return;
+ }
+
+ long long wrapperToken = mProto->start(TYPE_MESSAGE + FIELD_ID_DATA);
+ mProto->write(TYPE_INT64 + FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs());
+ long long eventToken = mProto->start(TYPE_MESSAGE + FIELD_ID_STATS_EVENTS);
+ event.ToProto(*mProto);
+ mProto->end(eventToken);
+ mProto->end(wrapperToken);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
new file mode 100644
index 000000000000..879175cc25ff
--- /dev/null
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef EVENT_METRIC_PRODUCER_H
+#define EVENT_METRIC_PRODUCER_H
+
+#include <unordered_map>
+
+#include <android/util/ProtoOutputStream.h>
+#include "../condition/ConditionTracker.h"
+#include "../matchers/matcher_util.h"
+#include "MetricProducer.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "stats_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class EventMetricProducer : public MetricProducer {
+public:
+ // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
+ EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard);
+
+ virtual ~EventMetricProducer();
+
+ void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey,
+ bool condition, const LogEvent& event) override;
+
+ void onConditionChanged(const bool conditionMet) override;
+
+ void finish() override;
+
+ StatsLogReport onDumpReport() override;
+
+ void onSlicedConditionMayChange() override;
+
+ // TODO: Implement this later.
+ virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+
+private:
+ const EventMetric mMetric;
+
+ std::unique_ptr<android::util::ProtoOutputStream> mProto;
+
+ long long mProtoToken;
+
+ void startNewProtoOutputStream(long long timestamp);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // EVENT_METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
new file mode 100644
index 000000000000..3c8ce6e7dd1c
--- /dev/null
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+#include "MetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+
+void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+ uint64_t eventTimeNs = event.GetTimestampNs();
+ // this is old event, maybe statsd restarted?
+ if (eventTimeNs < mStartTimeNs) {
+ return;
+ }
+
+ HashableDimensionKey eventKey;
+
+ if (mDimension.size() > 0) {
+ vector<KeyValuePair> key = getDimensionKey(event, mDimension);
+ eventKey = getHashableKey(key);
+ // Add the HashableDimensionKey->vector<KeyValuePair> to the map, because StatsLogReport
+ // expects vector<KeyValuePair>.
+ if (mDimensionKeyMap.find(eventKey) == mDimensionKeyMap.end()) {
+ mDimensionKeyMap[eventKey] = key;
+ }
+ } else {
+ eventKey = DEFAULT_DIMENSION_KEY;
+ }
+
+ bool condition;
+
+ map<string, HashableDimensionKey> conditionKeys;
+ if (mConditionSliced) {
+ for (const auto& link : mConditionLinks) {
+ HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
+ conditionKeys[link.condition()] = conditionKey;
+ }
+ if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
+ condition = false;
+ } else {
+ condition = true;
+ }
+ } else {
+ condition = mCondition;
+ }
+
+ onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index afaab648c09f..496b1453e46c 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -48,7 +48,7 @@ public:
virtual ~MetricProducer(){};
// Consume the parsed stats log entry that already matched the "what" of the metric.
- virtual void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) = 0;
+ void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event);
virtual void onConditionChanged(const bool condition) = 0;
@@ -86,6 +86,26 @@ protected:
std::unordered_map<HashableDimensionKey, std::vector<KeyValuePair>> mDimensionKeyMap;
std::vector<EventConditionLink> mConditionLinks;
+
+ /*
+ * Individual metrics can implement their own business logic here. All pre-processing is done.
+ *
+ * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
+ * DurationMetric, because it has start/stop/stop_all 3 matchers.
+ * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
+ * dimensions, it will be DEFAULT_DIMENSION_KEY
+ * [conditionKey]: the keys of conditions which should be used to query the condition for this
+ * target event (from EventConditionLink). This is passed to individual metrics
+ * because DurationMetric needs it to be cached.
+ * [condition]: whether condition is met. If condition is sliced, this is the result coming from
+ * query with ConditionWizard; If condition is not sliced, this is the
+ * nonSlicedCondition.
+ * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
+ */
+ virtual void onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index c19d46298de5..1ffa58b8c862 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -43,7 +43,7 @@ MetricsManager::MetricsManager(const StatsdConfig& config) {
}
MetricsManager::~MetricsManager() {
- VLOG("~MetricManager()");
+ VLOG("~MetricsManager()");
}
bool MetricsManager::isConfigValid() const {
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 56f57d3d3654..2f91460061fa 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -51,11 +51,11 @@ private:
std::set<int> mTagIds;
// We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in
- // MetricManager. There are relationship between them, and the relationship are denoted by index
- // instead of pointers. The reasons for this are: (1) the relationship between them are
- // complicated, store index instead of pointers reduce the risk of A holds B's sp, and B holds
- // A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get the
- // related results from a cache using the index.
+ // MetricsManager. There are relationships between them, and the relationships are denoted by
+ // index instead of pointers. The reasons for this are: (1) the relationship between them are
+ // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B
+ // holds A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get
+ // the related results from a cache using the index.
// Hold all the log entry matchers from the config.
std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 23071aa0899c..e90f998a7179 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -20,6 +20,7 @@
#include "../matchers/SimpleLogMatchingTracker.h"
#include "CountMetricProducer.h"
#include "DurationMetricProducer.h"
+#include "EventMetricProducer.h"
#include "stats_util.h"
using std::set;
@@ -31,13 +32,51 @@ namespace android {
namespace os {
namespace statsd {
-int getTrackerIndex(const string& name, const unordered_map<string, int>& logTrackerMap) {
- auto logTrackerIt = logTrackerMap.find(name);
+bool handleMetricWithLogTrackers(const string what, const int metricIndex,
+ const unordered_map<string, int>& logTrackerMap,
+ unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ int& logTrackerIndex) {
+ auto logTrackerIt = logTrackerMap.find(what);
if (logTrackerIt == logTrackerMap.end()) {
- ALOGW("cannot find the LogEventMatcher %s in config", name.c_str());
- return MATCHER_NOT_FOUND;
+ ALOGW("cannot find the LogEntryMatcher %s in config", what.c_str());
+ return false;
+ }
+ logTrackerIndex = logTrackerIt->second;
+ auto& metric_list = trackerToMetricMap[logTrackerIndex];
+ metric_list.push_back(metricIndex);
+ return true;
+}
+
+bool handleMetricWithConditions(
+ const string condition, const int metricIndex,
+ const unordered_map<string, int>& conditionTrackerMap,
+ const ::google::protobuf::RepeatedPtrField<::android::os::statsd::EventConditionLink>&
+ links,
+ vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
+ unordered_map<int, std::vector<int>>& conditionToMetricMap) {
+ auto condition_it = conditionTrackerMap.find(condition);
+ if (condition_it == conditionTrackerMap.end()) {
+ ALOGW("cannot find the Condition %s in the config", condition.c_str());
+ return false;
}
- return logTrackerIt->second;
+
+ for (const auto& link : links) {
+ auto it = conditionTrackerMap.find(link.condition());
+ if (it == conditionTrackerMap.end()) {
+ ALOGW("cannot find the Condition %s in the config", link.condition().c_str());
+ return false;
+ }
+ allConditionTrackers[condition_it->second]->setSliced(true);
+ allConditionTrackers[it->second]->setSliced(true);
+ allConditionTrackers[it->second]->addDimensions(
+ vector<KeyMatcher>(link.key_in_condition().begin(), link.key_in_condition().end()));
+ }
+ conditionIndex = condition_it->second;
+
+ // will create new vector if not exist before.
+ auto& metricList = conditionToMetricMap[condition_it->second];
+ metricList.push_back(metricIndex);
+ return true;
}
bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
@@ -142,7 +181,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
unordered_map<int, std::vector<int>>& conditionToMetricMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
- const int allMetricsCount = config.count_metric_size() + config.duration_metric_size();
+ const int allMetricsCount =
+ config.count_metric_size() + config.duration_metric_size() + config.event_metric_size();
allMetricProducers.reserve(allMetricsCount);
// Build MetricProducers for each metric defined in config.
@@ -155,100 +195,52 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
}
int metricIndex = allMetricProducers.size();
-
- auto logTrackerIt = logTrackerMap.find(metric.what());
- if (logTrackerIt == logTrackerMap.end()) {
- ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
+ int trackerIndex;
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
return false;
}
- int logTrackerIndex = logTrackerIt->second;
- auto& metric_list = trackerToMetricMap[logTrackerIndex];
- metric_list.push_back(metricIndex);
-
- sp<MetricProducer> countProducer;
+ int conditionIndex = -1;
if (metric.has_condition()) {
- auto condition_it = conditionTrackerMap.find(metric.condition());
- if (condition_it == conditionTrackerMap.end()) {
- ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
- return false;
- }
-
- for (const auto& link : metric.links()) {
- auto it = conditionTrackerMap.find(link.condition());
- if (it == conditionTrackerMap.end()) {
- ALOGW("cannot find the Condition %s in the config", link.condition().c_str());
- return false;
- }
- allConditionTrackers[condition_it->second]->setSliced(true);
- allConditionTrackers[it->second]->setSliced(true);
- allConditionTrackers[it->second]->addDimensions(vector<KeyMatcher>(
- link.key_in_condition().begin(), link.key_in_condition().end()));
- }
-
- countProducer = new CountMetricProducer(metric, condition_it->second, wizard);
- // will create new vector if not exist before.
- auto& metricList = conditionToMetricMap[condition_it->second];
- metricList.push_back(metricIndex);
- } else {
- countProducer = new CountMetricProducer(metric, -1 /*no condition*/, wizard);
+ handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap);
}
+
+ sp<MetricProducer> countProducer = new CountMetricProducer(metric, conditionIndex, wizard);
allMetricProducers.push_back(countProducer);
}
for (int i = 0; i < config.duration_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
- const DurationMetric metric = config.duration_metric(i);
- if (!metric.has_start()) {
- ALOGW("cannot find start in DurationMetric %lld", metric.metric_id());
+ const DurationMetric& metric = config.duration_metric(i);
+ int trackerIndices[3] = {-1, -1, -1};
+ if (!metric.has_start() ||
+ !handleMetricWithLogTrackers(metric.start(), metricIndex, logTrackerMap,
+ trackerToMetricMap, trackerIndices[0])) {
+ ALOGE("Duration metrics must specify a valid the start event matcher");
return false;
}
- int trackerIndices[] = {-1, -1, -1};
- trackerIndices[0] = getTrackerIndex(metric.start(), logTrackerMap);
-
- if (metric.has_stop()) {
- trackerIndices[1] = getTrackerIndex(metric.stop(), logTrackerMap);
- }
-
- if (metric.has_stop_all()) {
- trackerIndices[2] = getTrackerIndex(metric.stop_all(), logTrackerMap);
+ if (metric.has_stop() &&
+ !handleMetricWithLogTrackers(metric.stop(), metricIndex, logTrackerMap,
+ trackerToMetricMap, trackerIndices[1])) {
+ return false;
}
- for (const int& index : trackerIndices) {
- if (index == MATCHER_NOT_FOUND) {
- return false;
- }
- if (index >= 0) {
- auto& metric_list = trackerToMetricMap[index];
- metric_list.push_back(metricIndex);
- }
+ if (metric.has_stop_all() &&
+ !handleMetricWithLogTrackers(metric.stop_all(), metricIndex, logTrackerMap,
+ trackerToMetricMap, trackerIndices[2])) {
+ return false;
}
int conditionIndex = -1;
if (metric.has_predicate()) {
- auto condition_it = conditionTrackerMap.find(metric.predicate());
- if (condition_it == conditionTrackerMap.end()) {
- ALOGW("cannot find the Condition %s in the config", metric.predicate().c_str());
- return false;
- }
- conditionIndex = condition_it->second;
-
- for (const auto& link : metric.links()) {
- auto it = conditionTrackerMap.find(link.condition());
- if (it == conditionTrackerMap.end()) {
- ALOGW("cannot find the Condition %s in the config", link.condition().c_str());
- return false;
- }
- allConditionTrackers[condition_it->second]->setSliced(true);
- allConditionTrackers[it->second]->setSliced(true);
- allConditionTrackers[it->second]->addDimensions(vector<KeyMatcher>(
- link.key_in_condition().begin(), link.key_in_condition().end()));
- }
-
- auto& metricList = conditionToMetricMap[conditionIndex];
- metricList.push_back(metricIndex);
+ handleMetricWithConditions(metric.predicate(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap);
}
sp<MetricProducer> durationMetric =
@@ -257,6 +249,32 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
allMetricProducers.push_back(durationMetric);
}
+
+ for (int i = 0; i < config.event_metric_size(); i++) {
+ int metricIndex = allMetricProducers.size();
+ const EventMetric& metric = config.event_metric(i);
+ if (!metric.has_metric_id() || !metric.has_what()) {
+ ALOGW("cannot find the metric id or what in config");
+ return false;
+ }
+ int trackerIndex;
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return false;
+ }
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap);
+ }
+
+ sp<MetricProducer> eventMetric = new EventMetricProducer(metric, conditionIndex, wizard);
+
+ allMetricProducers.push_back(eventMetric);
+ }
+
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 38149a6aecd6..6722eb3cfe72 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -22,7 +22,6 @@
#include "../condition/ConditionTracker.h"
#include "../matchers/LogMatchingTracker.h"
-#include "CountMetricProducer.h"
namespace android {
namespace os {
@@ -81,7 +80,7 @@ bool initMetrics(
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap);
-// Initialize MetricManager from StatsdConfig.
+// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
@@ -91,8 +90,6 @@ bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
-int getTrackerIndex(const std::string& name, const std::unordered_map<string, int>& logTrackerMap);
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
index cd00ba8e8898..74ee3325a943 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -36,9 +36,41 @@ option java_outer_classname = "StatsEventProto";
*/
message StatsEvent {
oneof event {
- ScreenStateChanged screen_state_changed = 1;
- ProcessStateChanged process_state_changed = 2;
- WakeLockChanged wakelock_changed = 3;
+ // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
+ BleScanStateChanged ble_scan_state_changed = 2;
+ BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
+ BleScanResultReceived ble_scan_result_received = 4;
+ SensorStateChanged sensor_state_changed = 5;
+ GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested
+ SyncStateChanged sync_state_changed = 7;
+ ScheduledJobStateChanged scheduled_job_state_changed = 8;
+ ScreenBrightnessChanged screen_brightness_changed = 9;
+ // 10-20 are temporarily reserved for wakelocks etc.
+ WakelockStateChanged wakelock_state_changed = 10;
+ UidWakelockStateChanged uid_wakelock_state_changed = 11;
+ LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 12;
+ BatterySaverModeStateChanged battery_saver_mode_state_changed = 21;
+ DeviceIdleModeStateChanged device_idle_mode_state_changed = 22;
+ AudioStateChanged audio_state_changed = 23;
+ MediaCodecActivityChanged media_codec_activity_changed = 24;
+ CameraStateChanged camera_state_changed = 25;
+ FlashlightStateChanged flashlight_state_changed = 26;
+ UidProcessStateChanged uid_process_state_changed = 27;
+ ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28;
+ ScreenStateChanged screen_state_changed = 29;
+ BatteryLevelChanged battery_level_changed = 30;
+ ChargingStateChanged charging_state_changed = 31;
+ PluggedStateChanged plugged_state_changed = 32;
+ DeviceTemperatureReported device_temperature_reported = 33;
+ DeviceOnStatusChanged device_on_status_changed = 34;
+ WakeupAlarmOccurred wakeup_alarm_occurred = 35;
+ KernelWakeupReported kernel_wakeup_reported = 36;
+ WifiLockStateChanged wifi_lock_state_changed = 37;
+ WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
+ WifiScanStateChanged wifi_scan_state_changed = 39;
+ PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
+
+ // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
}
@@ -72,7 +104,7 @@ message WorkSource {
* and those UIDs will be translated in xxx to those strings.
*
* CONVENTIONS:
- * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange
+ * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange.
* - If there is a UID, it goes first. Think in an object-oriented fashion.
* *****************************************************************************
*/
@@ -98,33 +130,519 @@ message ScreenStateChanged {
}
/**
- * Logs that the state of a process state, as per the activity manager has changed.
+ * Logs that the state of a process state, as per the activity manager, has changed.
*
* Logged from:
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
*/
-message ProcessStateChanged {
- // TODO: Use the real (mapped) process states.
+message UidProcessStateChanged {
optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
// The state.
+ // TODO: Use the real (mapped) process states.
optional int32 state = 2;
}
/**
- * Logs that the state of a wakelock has changed.
+ * Logs that a process started, finished, crashed, or ANRed.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message ProcessLifeCycleStateChanged {
+ // TODO: Use the real (mapped) process states.
+ optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
+
+ // TODO: What is this?
+ optional string name = 2;
+
+ // The state.
+ // TODO: Use an enum.
+ optional int32 event = 3;
+}
+
+
+
+/**
+ * Logs when the ble scan state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message BleScanStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when an unoptimized ble scan state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+message BleUnoptimizedScanStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs reporting of a ble scan finding results.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+message BleScanResultReceived {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Number of ble scan results returned.
+ optional int32 num_of_results = 2;
+}
+
+/**
+ * Logs when a sensor state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message SensorStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // TODO: Is there a way to get the actual name of the sensor?
+ // The id (int) of the sensor.
+ optional int32 sensor_id = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+}
+
+
+/**
+ * Logs when GPS state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message GpsScanStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+
+/**
+ * Logs when a sync manager sync state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message SyncStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Name of the sync (as named in the app)
+ optional string name = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+}
+
+/**
+ * Logs when a job scheduler job state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message ScheduledJobStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Name of the job (as named in the app)
+ optional string name = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+
+ // TODO: Consider adding the stopReason (int)
+}
+
+/**
+ * Logs when the audio state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message AudioStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when the video codec state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message MediaCodecActivityChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when the flashlight state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message FlashlightStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when the camera state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message CameraStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs that the state of a wakelock (per app and per wakelock name) has changed.
*
* Logged from:
* TODO
*/
-message WakeLockChanged {
+message WakelockStateChanged {
// TODO: Add attribution instead of uid.
optional int32 uid = 1;
+ // Type of wakelock.
+ enum Type {
+ PARTIAL = 0;
+ FULL = 1;
+ WINDOW = 2;
+ }
+ optional int32 type = 2;
+
+ // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
+ optional string tag = 3;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 4;
+}
+
+/**
+ * Logs when an app is holding a wakelock, regardless of the wakelock's name.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message UidWakelockStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Type of wakelock.
+ enum Type {
+ PARTIAL = 0;
+ FULL = 1;
+ WINDOW = 2;
+ }
+ optional int32 type = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+}
+
+/**
+ * Logs when a partial wakelock is considered 'long' (over 1 min).
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message LongPartialWakelockStateChanged {
+ // TODO: Add attribution instead of uid?
+ optional int32 uid = 1;
+
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
optional string tag = 2;
- // TODO: Use a constant instead of boolean?
- optional bool state = 3;
+ // TODO: I have no idea what this is.
+ optional string history_tag = 3;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 4;
+}
+
+/**
+ * Logs Battery Saver state change.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message BatterySaverModeStateChanged {
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 1;
+}
+
+/**
+ * Logs Doze mode state change.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message DeviceIdleModeStateChanged {
+ // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_.
+ optional int32 state = 1;
}
+/**
+ * Logs screen brightness level.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message ScreenBrightnessChanged {
+ // Screen brightness level. Should be in [-1, 255] according to PowerManager.java.
+ optional int32 level = 1;
+}
+
+/**
+ * Logs battery level (percent full, from 0 to 100).
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message BatteryLevelChanged {
+ // Battery level. Should be in [0, 100].
+ optional int32 battery_level = 1;
+}
+
+/**
+ * Logs change in charging status of the device.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message ChargingStateChanged {
+ // TODO: Link directly to BatteryManager.java's constants (via a proto).
+ enum State {
+ BATTERY_STATUS_UNKNOWN = 1;
+ BATTERY_STATUS_CHARGING = 2;
+ BATTERY_STATUS_DISCHARGING = 3;
+ BATTERY_STATUS_NOT_CHARGING = 4;
+ BATTERY_STATUS_FULL = 5;
+ }
+ optional State charging_state = 1;
+}
+
+/**
+ * Logs whether the device is plugged in, and what power source it is using.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message PluggedStateChanged {
+ // TODO: Link directly to BatteryManager.java's constants (via a proto).
+ enum State {
+ // Note that NONE is not in BatteryManager.java's constants.
+ BATTERY_PLUGGED_NONE = 0;
+ // Power source is an AC charger.
+ BATTERY_PLUGGED_AC = 1;
+ // Power source is a USB port.
+ BATTERY_PLUGGED_USB = 2;
+ // Power source is wireless.
+ BATTERY_PLUGGED_WIRELESS = 4;
+ }
+ optional State plugged_state = 1;
+}
+
+/**
+ * Logs the temperature of the device, in tenths of a degree Celsius.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message DeviceTemperatureReported {
+ // Temperature in tenths of a degree C.
+ optional int32 temperature = 1;
+}
+
+// TODO: Define this more precisely.
+// TODO: Log the ON state somewhere. It isn't currently logged anywhere.
+/**
+ * Logs when the device turns off or on.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message DeviceOnStatusChanged {
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 1;
+}
+
+/**
+ * Logs when an app's wakeup alarm fires.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message WakeupAlarmOccurred {
+ // TODO: Add attribution instead of uid?
+ optional int32 uid = 1;
+}
+
+/**
+ * Logs kernel wakeup reasons and aborts.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message KernelWakeupReported {
+ // Name of the kernel wakeup reason (or abort).
+ optional string wakeup_reason_name = 1;
+
+ // Duration (in microseconds) for the wake-up interrupt to be serviced.
+ optional int64 duration_usec = 2;
+}
+
+/**
+ * Logs wifi locks held by an app.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message WifiLockStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs wifi signal strength changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message WifiSignalStrengthChanged {
+ // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
+ enum SignalStrength {
+ SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+ SIGNAL_STRENGTH_POOR = 1;
+ SIGNAL_STRENGTH_MODERATE = 2;
+ SIGNAL_STRENGTH_GOOD = 3;
+ SIGNAL_STRENGTH_GREAT = 4;
+ }
+ optional SignalStrength signal_strength = 1;
+}
+
+/**
+ * Logs wifi scans performed by an app.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message WifiScanStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs phone signal strength changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message PhoneSignalStrengthChanged {
+ // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
+ enum SignalStrength {
+ SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+ SIGNAL_STRENGTH_POOR = 1;
+ SIGNAL_STRENGTH_MODERATE = 2;
+ SIGNAL_STRENGTH_GOOD = 3;
+ SIGNAL_STRENGTH_GREAT = 4;
+ }
+ optional SignalStrength signal_strength = 1;
+} \ No newline at end of file
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 575588b3ba72..39c1d59c88b7 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -30,6 +30,14 @@ namespace statsd {
#define MATCHER_NOT_FOUND -2
#define NANO_SECONDS_IN_A_SECOND (1000 * 1000 * 1000)
+// TODO: Remove the following constants once they are exposed in ProtOutputStream.h
+const uint64_t FIELD_TYPE_SHIFT = 32;
+const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT;
+const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT;
+const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT;
+const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT;
+const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT;
+
typedef std::string HashableDimensionKey;
EventMetricData parse(log_msg msg);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index afb3f2b0db25..113ac62699d2 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -114,7 +114,7 @@ message Alert {
optional int32 refractory_period_secs = 4;
- optional int64 trigger_if_gt = 5;
+ optional int64 trigger_if_sum_gt = 5;
}
message EventMetric {
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index f32c0d68c851..47e148df1023 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -709,9 +709,9 @@ android.bluetooth.BluetoothHeadset$1
android.bluetooth.BluetoothHeadset$2
android.bluetooth.BluetoothHeadset$3
android.bluetooth.BluetoothHealthAppConfiguration
-android.bluetooth.BluetoothInputDevice
-android.bluetooth.BluetoothInputDevice$1
-android.bluetooth.BluetoothInputDevice$2
+android.bluetooth.BluetoothHidHost
+android.bluetooth.BluetoothHidHost$1
+android.bluetooth.BluetoothHidHost$2
android.bluetooth.BluetoothInputStream
android.bluetooth.BluetoothManager
android.bluetooth.BluetoothMap
@@ -754,9 +754,9 @@ android.bluetooth.IBluetoothHeadsetPhone$Stub$Proxy
android.bluetooth.IBluetoothHealth
android.bluetooth.IBluetoothHealth$Stub
android.bluetooth.IBluetoothHealthCallback
-android.bluetooth.IBluetoothInputDevice
-android.bluetooth.IBluetoothInputDevice$Stub
-android.bluetooth.IBluetoothInputDevice$Stub$Proxy
+android.bluetooth.IBluetoothHidHost
+android.bluetooth.IBluetoothHidHost$Stub
+android.bluetooth.IBluetoothHidHost$Stub$Proxy
android.bluetooth.IBluetoothManager
android.bluetooth.IBluetoothManager$Stub
android.bluetooth.IBluetoothManager$Stub$Proxy
diff --git a/core/java/Android.bp b/core/java/Android.bp
new file mode 100644
index 000000000000..42b0f6bad0ae
--- /dev/null
+++ b/core/java/Android.bp
@@ -0,0 +1,4 @@
+filegroup {
+ name: "IKeyAttestationApplicationIdProvider.aidl",
+ srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fc4c8d7f0666..8bc2073c3cda 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -46,6 +46,7 @@ import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.BatteryStats;
+import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -500,7 +501,7 @@ public class ActivityManager {
public static final int PROCESS_STATE_SERVICE = 11;
/** @hide Process is in the background running a receiver. Note that from the
- * perspective of oom_adj receivers run at a higher foreground level, but for our
+ * perspective of oom_adj, receivers run at a higher foreground level, but for our
* prioritization here that is not necessary and putting them below services means
* many fewer changes in some process states as they receive broadcasts. */
public static final int PROCESS_STATE_RECEIVER = 12;
@@ -524,6 +525,20 @@ public class ActivityManager {
/** @hide Process does not exist. */
public static final int PROCESS_STATE_NONEXISTENT = 18;
+ // NOTE: If PROCESS_STATEs are added or changed, then new fields must be added
+ // to frameworks/base/core/proto/android/app/activitymanager.proto and the following method must
+ // be updated to correctly map between them.
+ /**
+ * Maps ActivityManager.PROCESS_STATE_ values to ActivityManagerProto.ProcessState enum.
+ *
+ * @param amInt a process state of the form ActivityManager.PROCESS_STATE_
+ * @return the value of the corresponding android.app.ActivityManagerProto's ProcessState enum.
+ * @hide
+ */
+ public static final int processStateAmToProto(int amInt) {
+ return amInt * 100;
+ }
+
/** @hide The lowest process state number */
public static final int MIN_PROCESS_STATE = PROCESS_STATE_PERSISTENT;
@@ -1596,6 +1611,9 @@ public class ActivityManager {
public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags)
throws SecurityException {
try {
+ if (maxNum < 0) {
+ throw new IllegalArgumentException("The requested number of tasks should be >= 0");
+ }
return getService().getRecentTasks(maxNum, flags, UserHandle.myUserId()).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -3882,21 +3900,36 @@ public class ActivityManager {
IBinder service = ServiceManager.checkService(name);
if (service == null) {
pw.println(" (Service not found)");
+ pw.flush();
return;
}
- TransferPipe tp = null;
- try {
- pw.flush();
- tp = new TransferPipe();
- tp.setBufferPrefix(" ");
- service.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
- tp.go(fd, 10000);
- } catch (Throwable e) {
- if (tp != null) {
- tp.kill();
+ pw.flush();
+ if (service instanceof Binder) {
+ // If this is a local object, it doesn't make sense to do an async dump with it,
+ // just directly dump.
+ try {
+ service.dump(fd, args);
+ } catch (Throwable e) {
+ pw.println("Failure dumping service:");
+ e.printStackTrace(pw);
+ pw.flush();
+ }
+ } else {
+ // Otherwise, it is remote, do the dump asynchronously to avoid blocking.
+ TransferPipe tp = null;
+ try {
+ pw.flush();
+ tp = new TransferPipe();
+ tp.setBufferPrefix(" ");
+ service.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
+ tp.go(fd, 10000);
+ } catch (Throwable e) {
+ if (tp != null) {
+ tp.kill();
+ }
+ pw.println("Failure dumping service:");
+ e.printStackTrace(pw);
}
- pw.println("Failure dumping service:");
- e.printStackTrace(pw);
}
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 2813e8b9707e..55f9e289f52d 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -33,6 +33,7 @@ import android.os.WorkSource;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import libcore.util.ZoneInfoDB;
@@ -48,7 +49,7 @@ import java.lang.annotation.RetentionPolicy;
* if it is not already running. Registered alarms are retained while the
* device is asleep (and can optionally wake the device up if they go off
* during that time), but will be cleared if it is turned off and rebooted.
- *
+ *
* <p>The Alarm Manager holds a CPU wake lock as long as the alarm receiver's
* onReceive() method is executing. This guarantees that the phone will not sleep
* until you have finished handling the broadcast. Once onReceive() returns, the
@@ -296,7 +297,7 @@ public class AlarmManager {
* {@link Intent#EXTRA_ALARM_COUNT Intent.EXTRA_ALARM_COUNT} that indicates
* how many past alarm events have been accumulated into this intent
* broadcast. Recurring alarms that have gone undelivered because the
- * phone was asleep may have a count greater than one when delivered.
+ * phone was asleep may have a count greater than one when delivered.
*
* <div class="note">
* <p>
@@ -396,10 +397,10 @@ public class AlarmManager {
* set a recurring alarm for the top of every hour but the phone was asleep
* from 7:45 until 8:45, an alarm will be sent as soon as the phone awakens,
* then the next alarm will be sent at 9:00.
- *
- * <p>If your application wants to allow the delivery times to drift in
+ *
+ * <p>If your application wants to allow the delivery times to drift in
* order to guarantee that at least a certain time interval always elapses
- * between alarms, then the approach to take is to use one-time alarms,
+ * between alarms, then the approach to take is to use one-time alarms,
* scheduling the next one yourself when handling each alarm delivery.
*
* <p class="note">
@@ -1056,7 +1057,7 @@ public class AlarmManager {
/**
* Creates a new alarm clock description.
*
- * @param triggerTime time at which the underlying alarm is triggered in wall time
+ * @param triggerTime time at which the underlying alarm is triggered in wall time
* milliseconds since the epoch
* @param showIntent an intent that can be used to show or edit details of
* the alarm clock.
@@ -1089,7 +1090,7 @@ public class AlarmManager {
* Returns an intent that can be used to show or edit details of the alarm clock in
* the application that scheduled it.
*
- * <p class="note">Beware that any application can retrieve and send this intent,
+ * <p class="note">Beware that any application can retrieve and send this intent,
* potentially with additional fields filled in. See
* {@link PendingIntent#send(android.content.Context, int, android.content.Intent)
* PendingIntent.send()} and {@link android.content.Intent#fillIn Intent.fillIn()}
@@ -1121,5 +1122,13 @@ public class AlarmManager {
return new AlarmClockInfo[size];
}
};
+
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(AlarmClockInfoProto.TRIGGER_TIME_MS, mTriggerTime);
+ mShowIntent.writeToProto(proto, AlarmClockInfoProto.SHOW_INTENT);
+ proto.end(token);
+ }
}
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index a25c2267214e..baeabc39e3fb 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -33,6 +33,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AndroidException;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1081,7 +1082,16 @@ public final class PendingIntent implements Parcelable {
sb.append('}');
return sb.toString();
}
-
+
+ /** @hide */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ if (mTarget != null) {
+ proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString());
+ }
+ proto.end(token);
+ }
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3c5306331716..ab8edee724f2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6533,6 +6533,52 @@ public class DevicePolicyManager {
}
/**
+ * Called by device owner to set the system wall clock time. This only takes effect if called
+ * when {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
+ * returned.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param millis time in milliseconds since the Epoch
+ * @return {@code true} if set time succeeded, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public boolean setTime(@NonNull ComponentName admin, long millis) {
+ throwIfParentInstance("setTime");
+ if (mService != null) {
+ try {
+ return mService.setTime(admin, millis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by device owner to set the system's persistent default time zone. This only takes
+ * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise
+ * {@code false} will be returned.
+ *
+ * @see android.app.AlarmManager#setTimeZone(String)
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param timeZone one of the Olson ids from the list returned by
+ * {@link java.util.TimeZone#getAvailableIDs}
+ * @return {@code true} if set timezone succeeded, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) {
+ throwIfParentInstance("setTimeZone");
+ if (mService != null) {
+ try {
+ return mService.setTimeZone(admin, timeZone);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by profile or device owners to update {@link android.provider.Settings.Secure}
* settings. Validation that the value of the setting is in the correct form for the setting
* type should be performed by the caller.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8865a0525292..e77c18636594 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -229,6 +229,9 @@ interface IDevicePolicyManager {
void setGlobalSetting(in ComponentName who, in String setting, in String value);
void setSecureSetting(in ComponentName who, in String setting, in String value);
+ boolean setTime(in ComponentName who, long millis);
+ boolean setTimeZone(in ComponentName who, String timeZone);
+
void setMasterVolumeMuted(in ComponentName admin, boolean on);
boolean isMasterVolumeMuted(in ComponentName admin);
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 1434c9baadf3..1cde73a0af61 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -909,12 +909,21 @@ public class JobInfo implements Parcelable {
}
/**
- * Set some description of the kind of network type your job needs to have.
- * Not calling this function means the network is not necessary, as the default is
- * {@link #NETWORK_TYPE_NONE}.
- * Bear in mind that calling this function defines network as a strict requirement for your
- * job. If the network requested is not available your job will never run. See
- * {@link #setOverrideDeadline(long)} to change this behaviour.
+ * Set some description of the kind of network type your job needs to
+ * have. Not calling this function means the network is not necessary,
+ * as the default is {@link #NETWORK_TYPE_NONE}. Bear in mind that
+ * calling this function defines network as a strict requirement for
+ * your job. If the network requested is not available your job will
+ * never run. See {@link #setOverrideDeadline(long)} to change this
+ * behaviour.
+ * <p class="note">
+ * Note: When your job executes in
+ * {@link JobService#onStartJob(JobParameters)}, be sure to use the
+ * specific network returned by {@link JobParameters#getNetwork()},
+ * otherwise you'll use the default network which may not meet this
+ * constraint.
+ *
+ * @see JobParameters#getNetwork()
*/
public Builder setRequiredNetworkType(@NetworkType int networkType) {
mNetworkType = networkType;
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index a6f6be22809c..5053dc6fdf05 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.IJobCallback;
import android.content.ClipData;
+import android.net.Network;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -66,6 +67,7 @@ public class JobParameters implements Parcelable {
private final boolean overrideDeadlineExpired;
private final Uri[] mTriggeredContentUris;
private final String[] mTriggeredContentAuthorities;
+ private final Network network;
private int stopReason; // Default value of stopReason is REASON_CANCELED
@@ -73,7 +75,7 @@ public class JobParameters implements Parcelable {
public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
Bundle transientExtras, ClipData clipData, int clipGrantFlags,
boolean overrideDeadlineExpired, Uri[] triggeredContentUris,
- String[] triggeredContentAuthorities) {
+ String[] triggeredContentAuthorities, Network network) {
this.jobId = jobId;
this.extras = extras;
this.transientExtras = transientExtras;
@@ -83,6 +85,7 @@ public class JobParameters implements Parcelable {
this.overrideDeadlineExpired = overrideDeadlineExpired;
this.mTriggeredContentUris = triggeredContentUris;
this.mTriggeredContentAuthorities = triggeredContentAuthorities;
+ this.network = network;
}
/**
@@ -171,6 +174,28 @@ public class JobParameters implements Parcelable {
}
/**
+ * Return the network that should be used to perform any network requests
+ * for this job.
+ * <p>
+ * Devices may have multiple active network connections simultaneously, or
+ * they may not have a default network route at all. To correctly handle all
+ * situations like this, your job should always use the network returned by
+ * this method instead of implicitly using the default network route.
+ * <p>
+ * Note that the system may relax the constraints you originally requested,
+ * such as allowing a {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over
+ * a metered network when there is a surplus of metered data available.
+ *
+ * @return the network that should be used to perform any network requests
+ * for this job, or {@code null} if this job didn't set any required
+ * network type.
+ * @see JobInfo.Builder#setRequiredNetworkType(int)
+ */
+ public @Nullable Network getNetwork() {
+ return network;
+ }
+
+ /**
* Dequeue the next pending {@link JobWorkItem} from these JobParameters associated with their
* currently running job. Calling this method when there is no more work available and all
* previously dequeued work has been completed will result in the system taking care of
@@ -257,6 +282,11 @@ public class JobParameters implements Parcelable {
overrideDeadlineExpired = in.readInt() == 1;
mTriggeredContentUris = in.createTypedArray(Uri.CREATOR);
mTriggeredContentAuthorities = in.createStringArray();
+ if (in.readInt() != 0) {
+ network = Network.CREATOR.createFromParcel(in);
+ } else {
+ network = null;
+ }
stopReason = in.readInt();
}
@@ -286,6 +316,12 @@ public class JobParameters implements Parcelable {
dest.writeInt(overrideDeadlineExpired ? 1 : 0);
dest.writeTypedArray(mTriggeredContentUris, flags);
dest.writeStringArray(mTriggeredContentAuthorities);
+ if (network != null) {
+ dest.writeInt(1);
+ network.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
dest.writeInt(stopReason);
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index df87b4556ee5..33825b4b5f41 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -156,27 +156,34 @@ public abstract class SliceProvider extends ContentProvider {
}
private Slice handleBindSlice(Uri sliceUri) {
- Slice[] output = new Slice[1];
- CountDownLatch latch = new CountDownLatch(1);
- Handler mainHandler = new Handler(Looper.getMainLooper());
- mainHandler.post(() -> {
- ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
- try {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .penaltyDeath()
- .build());
- output[0] = onBindSlice(sliceUri);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return onBindSliceStrict(sliceUri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Slice[] output = new Slice[1];
+ Handler.getMain().post(() -> {
+ output[0] = onBindSliceStrict(sliceUri);
latch.countDown();
+ });
+ try {
+ latch.await();
+ return output[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
}
- });
+ }
+ }
+
+ private Slice onBindSliceStrict(Uri sliceUri) {
+ ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
- latch.await();
- return output[0];
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectAll()
+ .penaltyDeath()
+ .build());
+ return onBindSlice(sliceUri);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
}
diff --git a/core/java/android/app/usage/AppStandby.java b/core/java/android/app/usage/AppStandby.java
new file mode 100644
index 000000000000..6f9fc2fa5d36
--- /dev/null
+++ b/core/java/android/app/usage/AppStandby.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 android.app.usage;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Set of constants for app standby buckets and reasons. Apps will be moved into different buckets
+ * that affect how frequently they can run in the background or perform other battery-consuming
+ * actions. Buckets will be assigned based on how frequently or when the system thinks the user
+ * is likely to use the app.
+ * @hide
+ */
+public class AppStandby {
+
+ /** The app was used very recently, currently in use or likely to be used very soon. */
+ public static final int STANDBY_BUCKET_ACTIVE = 0;
+
+ // Leave some gap in case we want to increase the number of buckets
+
+ /** The app was used recently and/or likely to be used in the next few hours */
+ public static final int STANDBY_BUCKET_WORKING_SET = 3;
+
+ // Leave some gap in case we want to increase the number of buckets
+
+ /** The app was used in the last few days and/or likely to be used in the next few days */
+ public static final int STANDBY_BUCKET_FREQUENT = 6;
+
+ // Leave some gap in case we want to increase the number of buckets
+
+ /** The app has not be used for several days and/or is unlikely to be used for several days */
+ public static final int STANDBY_BUCKET_RARE = 9;
+
+ // Leave some gap in case we want to increase the number of buckets
+
+ /** The app has never been used. */
+ public static final int STANDBY_BUCKET_NEVER = 12;
+
+ /** Reason for bucketing -- default initial state */
+ public static final String REASON_DEFAULT = "default";
+
+ /** Reason for bucketing -- timeout */
+ public static final String REASON_TIMEOUT = "timeout";
+
+ /** Reason for bucketing -- usage */
+ public static final String REASON_USAGE = "usage";
+
+ /** Reason for bucketing -- forced by user / shell command */
+ public static final String REASON_FORCED = "forced";
+
+ /**
+ * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
+ * be appended.
+ */
+ public static final String REASON_PREDICTED = "predicted";
+
+ @IntDef(flag = false, value = {
+ STANDBY_BUCKET_ACTIVE,
+ STANDBY_BUCKET_WORKING_SET,
+ STANDBY_BUCKET_FREQUENT,
+ STANDBY_BUCKET_RARE,
+ STANDBY_BUCKET_NEVER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StandbyBuckets {}
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 31b235977a04..4fbbdf2a9281 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -36,4 +36,6 @@ interface IUsageStatsManager {
void onCarrierPrivilegedAppsChanged();
void reportChooserSelection(String packageName, int userId, String contentType,
in String[] annotations, String action);
+ int getAppStandbyBucket(String packageName, String callingPackage, int userId);
+ void setAppStandbyBucket(String packageName, int bucket, int userId);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index fd579fce34d8..c827432a8b0b 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -19,6 +19,7 @@ package android.app.usage;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.usage.AppStandby.StandbyBuckets;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
@@ -247,6 +248,29 @@ public final class UsageStatsManager {
}
/**
+ * @hide
+ */
+ public @StandbyBuckets int getAppStandbyBucket(String packageName) {
+ try {
+ return mService.getAppStandbyBucket(packageName, mContext.getOpPackageName(),
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ }
+ return AppStandby.STANDBY_BUCKET_ACTIVE;
+ }
+
+ /**
+ * @hide
+ */
+ public void setAppStandbyBucket(String packageName, @StandbyBuckets int bucket) {
+ try {
+ mService.setAppStandbyBucket(packageName, bucket, mContext.getUserId());
+ } catch (RemoteException e) {
+ // Nothing to do
+ }
+ }
+
+ /**
* {@hide}
* Temporarily whitelist the specified app for a short duration. This is to allow an app
* receiving a high priority message to be able to access the network and acquire wakelocks
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index dc9970a7ca42..ab0eb92e1726 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -19,8 +19,6 @@ package android.appwidget;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Color;
@@ -66,11 +64,8 @@ public class AppWidgetHostView extends FrameLayout {
// When we're inflating the initialLayout for a AppWidget, we only allow
// views that are allowed in RemoteViews.
- static final LayoutInflater.Filter sInflaterFilter = new LayoutInflater.Filter() {
- public boolean onLoadClass(Class clazz) {
- return clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
- }
- };
+ private static final LayoutInflater.Filter INFLATER_FILTER =
+ (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
Context mContext;
Context mRemoteContext;
@@ -136,13 +131,19 @@ public class AppWidgetHostView extends FrameLayout {
mAppWidgetId = appWidgetId;
mInfo = info;
+ // We add padding to the AppWidgetHostView if necessary
+ Rect padding = getDefaultPadding();
+ setPadding(padding.left, padding.top, padding.right, padding.bottom);
+
// Sometimes the AppWidgetManager returns a null AppWidgetProviderInfo object for
// a widget, eg. for some widgets in safe mode.
if (info != null) {
- // We add padding to the AppWidgetHostView if necessary
- Rect padding = getDefaultPaddingForWidget(mContext, info.provider, null);
- setPadding(padding.left, padding.top, padding.right, padding.bottom);
- updateContentDescription(info);
+ String description = info.loadLabel(getContext().getPackageManager());
+ if ((info.providerInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0) {
+ description = Resources.getSystem().getString(
+ com.android.internal.R.string.suspended_widget_accessibility, description);
+ }
+ setContentDescription(description);
}
}
@@ -164,23 +165,23 @@ public class AppWidgetHostView extends FrameLayout {
*/
public static Rect getDefaultPaddingForWidget(Context context, ComponentName component,
Rect padding) {
- PackageManager packageManager = context.getPackageManager();
- ApplicationInfo appInfo;
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = context.getPackageManager().getApplicationInfo(component.getPackageName(), 0);
+ } catch (NameNotFoundException e) {
+ // if we can't find the package, ignore
+ }
+ return getDefaultPaddingForWidget(context, appInfo, padding);
+ }
+ private static Rect getDefaultPaddingForWidget(Context context, ApplicationInfo appInfo,
+ Rect padding) {
if (padding == null) {
padding = new Rect(0, 0, 0, 0);
} else {
padding.set(0, 0, 0, 0);
}
-
- try {
- appInfo = packageManager.getApplicationInfo(component.getPackageName(), 0);
- } catch (NameNotFoundException e) {
- // if we can't find the package, return 0 padding
- return padding;
- }
-
- if (appInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ if (appInfo != null && appInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Resources r = context.getResources();
padding.left = r.getDimensionPixelSize(com.android.internal.
R.dimen.default_app_widget_padding_left);
@@ -194,6 +195,11 @@ public class AppWidgetHostView extends FrameLayout {
return padding;
}
+ private Rect getDefaultPadding() {
+ return getDefaultPaddingForWidget(mContext,
+ mInfo == null ? null : mInfo.providerInfo.applicationInfo, null);
+ }
+
public int getAppWidgetId() {
return mAppWidgetId;
}
@@ -284,10 +290,7 @@ public class AppWidgetHostView extends FrameLayout {
newOptions = new Bundle();
}
- Rect padding = new Rect();
- if (mInfo != null) {
- padding = getDefaultPaddingForWidget(mContext, mInfo.provider, padding);
- }
+ Rect padding = getDefaultPadding();
float density = getResources().getDisplayMetrics().density;
int xPaddingDips = (int) ((padding.left + padding.right) / density);
@@ -361,7 +364,7 @@ public class AppWidgetHostView extends FrameLayout {
* initial layout.
*/
void resetAppWidget(AppWidgetProviderInfo info) {
- mInfo = info;
+ setAppWidget(mAppWidgetId, info);
mViewMode = VIEW_MODE_NOINIT;
updateAppWidget(null);
}
@@ -433,7 +436,6 @@ public class AppWidgetHostView extends FrameLayout {
}
applyContent(content, recycled, exception);
- updateContentDescription(mInfo);
}
private void applyContent(View content, boolean recycled, Exception exception) {
@@ -460,27 +462,6 @@ public class AppWidgetHostView extends FrameLayout {
}
}
- private void updateContentDescription(AppWidgetProviderInfo info) {
- if (info != null) {
- LauncherApps launcherApps = getContext().getSystemService(LauncherApps.class);
- ApplicationInfo appInfo = null;
- try {
- appInfo = launcherApps.getApplicationInfo(
- info.provider.getPackageName(), 0, info.getProfile());
- } catch (NameNotFoundException e) {
- // ignore -- use null.
- }
- if (appInfo != null &&
- (appInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0) {
- setContentDescription(
- Resources.getSystem().getString(
- com.android.internal.R.string.suspended_widget_accessibility, info.label));
- } else {
- setContentDescription(info.label);
- }
- }
- }
-
private void inflateAsync(RemoteViews remoteViews) {
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
@@ -614,7 +595,7 @@ public class AppWidgetHostView extends FrameLayout {
LayoutInflater inflater = (LayoutInflater)
theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater = inflater.cloneInContext(theirContext);
- inflater.setFilter(sInflaterFilter);
+ inflater.setFilter(INFLATER_FILTER);
AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
Bundle options = manager.getAppWidgetOptions(mAppWidgetId);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 3526e1896621..578a5b8b93e1 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1134,8 +1134,32 @@ public final class BluetoothAdapter {
}
/**
- * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of
- * the local Bluetooth adapter.
+ * Returns the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
+ * adapter.
+ *
+ * @return {@link BluetoothClass} Bluetooth CoD of local Bluetooth device.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public BluetoothClass getBluetoothClass() {
+ if (getState() != STATE_ON) return null;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.getBluetoothClass();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return null;
+ }
+
+ /**
+ * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth
+ * adapter.
+ *
+ * <p>Note: This value persists across system reboot.
*
* @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to.
* @return true if successful, false if unsuccessful.
@@ -2104,8 +2128,8 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.AVRCP_CONTROLLER) {
BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener);
return true;
- } else if (profile == BluetoothProfile.INPUT_DEVICE) {
- BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener);
+ } else if (profile == BluetoothProfile.HID_HOST) {
+ BluetoothHidHost iDev = new BluetoothHidHost(context, listener);
return true;
} else if (profile == BluetoothProfile.PAN) {
BluetoothPan pan = new BluetoothPan(context, listener);
@@ -2128,8 +2152,8 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.MAP_CLIENT) {
BluetoothMapClient mapClient = new BluetoothMapClient(context, listener);
return true;
- } else if (profile == BluetoothProfile.INPUT_HOST) {
- BluetoothInputHost iHost = new BluetoothInputHost(context, listener);
+ } else if (profile == BluetoothProfile.HID_DEVICE) {
+ BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
return true;
} else {
return false;
@@ -2167,8 +2191,8 @@ public final class BluetoothAdapter {
BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy;
avrcp.close();
break;
- case BluetoothProfile.INPUT_DEVICE:
- BluetoothInputDevice iDev = (BluetoothInputDevice) proxy;
+ case BluetoothProfile.HID_HOST:
+ BluetoothHidHost iDev = (BluetoothHidHost) proxy;
iDev.close();
break;
case BluetoothProfile.PAN:
@@ -2207,9 +2231,9 @@ public final class BluetoothAdapter {
BluetoothMapClient mapClient = (BluetoothMapClient) proxy;
mapClient.close();
break;
- case BluetoothProfile.INPUT_HOST:
- BluetoothInputHost iHost = (BluetoothInputHost) proxy;
- iHost.close();
+ case BluetoothProfile.HID_DEVICE:
+ BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
+ hidDevice.close();
break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index e18d9d1be51b..179f36dab32f 100644
--- a/core/java/android/bluetooth/BluetoothInputHost.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -33,9 +33,9 @@ import java.util.List;
/**
* @hide
*/
-public final class BluetoothInputHost implements BluetoothProfile {
+public final class BluetoothHidDevice implements BluetoothProfile {
- private static final String TAG = BluetoothInputHost.class.getSimpleName();
+ private static final String TAG = BluetoothHidDevice.class.getSimpleName();
/**
* Intent used to broadcast the change in connection state of the Input
@@ -57,7 +57,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
- "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED";
+ "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
/**
* Constants representing device subclass.
@@ -113,7 +113,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
private ServiceListener mServiceListener;
- private volatile IBluetoothInputHost mService;
+ private volatile IBluetoothHidDevice mService;
private BluetoothAdapter mAdapter;
@@ -205,23 +205,23 @@ public final class BluetoothInputHost implements BluetoothProfile {
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "onServiceConnected()");
- mService = IBluetoothInputHost.Stub.asInterface(service);
+ mService = IBluetoothHidDevice.Stub.asInterface(service);
if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST,
- BluetoothInputHost.this);
+ mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE,
+ BluetoothHidDevice.this);
}
}
public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "onServiceDisconnected()");
mService = null;
if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST);
+ mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE);
}
}
};
- BluetoothInputHost(Context context, ServiceListener listener) {
- Log.v(TAG, "BluetoothInputHost");
+ BluetoothHidDevice(Context context, ServiceListener listener) {
+ Log.v(TAG, "BluetoothHidDevice");
mContext = context;
mServiceListener = listener;
@@ -240,7 +240,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
}
boolean doBind() {
- Intent intent = new Intent(IBluetoothInputHost.class.getName());
+ Intent intent = new Intent(IBluetoothHidDevice.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
@@ -285,7 +285,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
Log.v(TAG, "getConnectedDevices()");
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
return service.getConnectedDevices();
@@ -306,7 +306,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -327,7 +327,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
Log.v(TAG, "getConnectionState(): device=" + device);
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
return service.getConnectionState(device);
@@ -367,7 +367,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
return false;
}
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
BluetoothHidDeviceAppConfiguration config =
@@ -401,7 +401,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
result = service.unregisterApp(config);
@@ -426,7 +426,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
result = service.sendReport(device, id, data);
@@ -454,7 +454,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
result = service.replyReport(device, type, id, data);
@@ -480,7 +480,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
result = service.reportError(device, error);
@@ -504,7 +504,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
result = service.unplug(device);
@@ -529,7 +529,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
result = service.connect(device);
@@ -553,7 +553,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- final IBluetoothInputHost service = mService;
+ final IBluetoothHidDevice service = mService;
if (service != null) {
try {
result = service.disconnect(device);
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 32615761cf8c..8ad0f9d064fd 100644
--- a/core/java/android/bluetooth/BluetoothInputDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -35,16 +35,16 @@ import java.util.List;
* This class provides the public APIs to control the Bluetooth Input
* Device Profile.
*
- * <p>BluetoothInputDevice is a proxy object for controlling the Bluetooth
+ * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth
* Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothInputDevice proxy object.
+ * the BluetoothHidHost proxy object.
*
* <p>Each method is protected with its appropriate permission.
*
* @hide
*/
-public final class BluetoothInputDevice implements BluetoothProfile {
- private static final String TAG = "BluetoothInputDevice";
+public final class BluetoothHidHost implements BluetoothProfile {
+ private static final String TAG = "BluetoothHidHost";
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -177,52 +177,52 @@ public final class BluetoothInputDevice implements BluetoothProfile {
* @hide
*/
public static final String EXTRA_PROTOCOL_MODE =
- "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE";
+ "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE";
/**
* @hide
*/
public static final String EXTRA_REPORT_TYPE =
- "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE";
+ "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE";
/**
* @hide
*/
public static final String EXTRA_REPORT_ID =
- "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID";
+ "android.bluetooth.BluetoothHidHost.extra.REPORT_ID";
/**
* @hide
*/
public static final String EXTRA_REPORT_BUFFER_SIZE =
- "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE";
+ "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE";
/**
* @hide
*/
- public static final String EXTRA_REPORT = "android.bluetooth.BluetoothInputDevice.extra.REPORT";
+ public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT";
/**
* @hide
*/
- public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS";
+ public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS";
/**
* @hide
*/
public static final String EXTRA_VIRTUAL_UNPLUG_STATUS =
- "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS";
+ "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS";
/**
* @hide
*/
public static final String EXTRA_IDLE_TIME =
- "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME";
+ "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME";
private Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- private volatile IBluetoothInputDevice mService;
+ private volatile IBluetoothHidHost mService;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
@@ -254,10 +254,10 @@ public final class BluetoothInputDevice implements BluetoothProfile {
};
/**
- * Create a BluetoothInputDevice proxy object for interacting with the local
+ * Create a BluetoothHidHost proxy object for interacting with the local
* Bluetooth Service which handles the InputDevice profile
*/
- /*package*/ BluetoothInputDevice(Context context, ServiceListener l) {
+ /*package*/ BluetoothHidHost(Context context, ServiceListener l) {
mContext = context;
mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -275,7 +275,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
}
boolean doBind() {
- Intent intent = new Intent(IBluetoothInputDevice.class.getName());
+ Intent intent = new Intent(IBluetoothHidHost.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
@@ -331,7 +331,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.connect(device);
@@ -371,7 +371,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.disconnect(device);
@@ -390,7 +390,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled()) {
try {
return service.getConnectedDevices();
@@ -409,7 +409,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled()) {
try {
return service.getDevicesMatchingConnectionStates(states);
@@ -428,7 +428,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getConnectionState(device);
@@ -458,7 +458,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
@@ -490,7 +490,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getPriority(device);
@@ -506,11 +506,11 @@ public final class BluetoothInputDevice implements BluetoothProfile {
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothInputDevice.Stub.asInterface(Binder.allowBlocking(service));
+ mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE,
- BluetoothInputDevice.this);
+ mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST,
+ BluetoothHidHost.this);
}
}
@@ -518,7 +518,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
if (DBG) Log.d(TAG, "Proxy object disconnected");
mService = null;
if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_DEVICE);
+ mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST);
}
}
};
@@ -542,7 +542,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.virtualUnplug(device);
@@ -568,7 +568,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getProtocolMode(device);
@@ -592,7 +592,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.setProtocolMode(device, protocolMode);
@@ -623,7 +623,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
+ "bufferSize=" + bufferSize);
}
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getReport(device, reportType, reportId, bufferSize);
@@ -649,7 +649,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.setReport(device, reportType, report);
@@ -674,7 +674,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.sendData(device, report);
@@ -698,7 +698,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.getIdleTime(device);
@@ -723,7 +723,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
- final IBluetoothInputDevice service = mService;
+ final IBluetoothHidHost service = mService;
if (service != null && isEnabled() && isValidDevice(device)) {
try {
return service.setIdleTime(device, idleTime);
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index bc8fa846087d..46a230b50605 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -73,11 +73,11 @@ public interface BluetoothProfile {
public static final int HEALTH = 3;
/**
- * Input Device Profile
+ * HID Host
*
* @hide
*/
- public static final int INPUT_DEVICE = 4;
+ public static final int HID_HOST = 4;
/**
* PAN Profile
@@ -152,11 +152,11 @@ public interface BluetoothProfile {
public static final int MAP_CLIENT = 18;
/**
- * Input Host
+ * HID Device
*
* @hide
*/
- public static final int INPUT_HOST = 19;
+ public static final int HID_DEVICE = 19;
/**
* Max profile ID. This value should be updated whenever a new profile is added to match
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index d73f8526843c..20342807f75f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -375,7 +375,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext
* traffic. Third-party libraries are encouraged to honor this flag as well.
*
- * <p>NOTE: {@code WebView} does not honor this flag.
+ * <p>NOTE: {@code WebView} honors this flag for applications targeting API level 26 and up.
*
* <p>This flag is ignored on Android N and above if an Android Network Security Config is
* present.
@@ -1463,98 +1463,84 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
}
- /**
- * @hide
- */
- public boolean isForwardLocked() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
+ /** @hide */
+ public boolean isDefaultToDeviceProtectedStorage() {
+ return (privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0;
}
- /**
- * @hide
- */
- @TestApi
- public boolean isSystemApp() {
- return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ /** @hide */
+ public boolean isDirectBootAware() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) != 0;
}
- /**
- * @hide
- */
- @TestApi
- public boolean isPrivilegedApp() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ /** @hide */
+ public boolean isEncryptionAware() {
+ return isDirectBootAware() || isPartiallyDirectBootAware();
}
- /**
- * @hide
- */
- public boolean isUpdatedSystemApp() {
- return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ /** @hide */
+ public boolean isExternal() {
+ return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
/** @hide */
- public boolean isInternal() {
- return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
+ public boolean isExternalAsec() {
+ return TextUtils.isEmpty(volumeUuid) && isExternal();
}
/** @hide */
- public boolean isExternalAsec() {
- return TextUtils.isEmpty(volumeUuid)
- && (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ public boolean isForwardLocked() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
}
/** @hide */
- public boolean isDefaultToDeviceProtectedStorage() {
- return (privateFlags
- & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0;
+ public boolean isInstantApp() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
}
/** @hide */
- public boolean isDirectBootAware() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) != 0;
+ public boolean isInternal() {
+ return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
}
/** @hide */
- public boolean isPartiallyDirectBootAware() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
+ public boolean isOem() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
}
/** @hide */
- public boolean isEncryptionAware() {
- return isDirectBootAware() || isPartiallyDirectBootAware();
+ public boolean isPartiallyDirectBootAware() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
}
- /**
- * @hide
- */
- public boolean isInstantApp() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+ /** @hide */
+ @TestApi
+ public boolean isPrivilegedApp() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
- /**
- * @hide
- */
+ /** @hide */
public boolean isRequiredForSystemUser() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
}
- /**
- * Returns true if the app has declared in its manifest that it wants its split APKs to be
- * loaded into isolated Contexts, with their own ClassLoaders and Resources objects.
- * @hide
- */
- public boolean requestsIsolatedSplitLoading() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
- }
-
- /**
- * @hide
- */
+ /** @hide */
public boolean isStaticSharedLibrary() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) != 0;
}
+ /** @hide */
+ @TestApi
+ public boolean isSystemApp() {
+ return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ /** @hide */
+ public boolean isUpdatedSystemApp() {
+ return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ }
+
/**
* Returns whether or not this application was installed as a virtual preload.
*/
@@ -1563,10 +1549,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
/**
+ * Returns true if the app has declared in its manifest that it wants its split APKs to be
+ * loaded into isolated Contexts, with their own ClassLoaders and Resources objects.
* @hide
*/
- public boolean isOem() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+ public boolean requestsIsolatedSplitLoading() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
}
/**
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 143c51da5367..14cf8557477b 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -337,6 +337,12 @@ public abstract class PackageManagerInternal {
public abstract boolean isPackagePersistent(String packageName);
/**
+ * Returns whether or not the given package represents a legacy system application released
+ * prior to runtime permissions.
+ */
+ public abstract boolean isLegacySystemApp(PackageParser.Package pkg);
+
+ /**
* Get all overlay packages for a user.
* @param userId The user for which to get the overlays.
* @return A list of overlay packages. An empty list is returned if the
@@ -467,7 +473,4 @@ public abstract class PackageManagerInternal {
/** Updates the flags for the given permission. */
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
@NonNull String packageName, int flagMask, int flagValues, int userId);
- /** Returns a PermissionGroup. */
- public abstract @Nullable PackageParser.PermissionGroup getPermissionGroupTEMP(
- @NonNull String groupName);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ad36139a2da4..b48829cfc87d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6221,48 +6221,48 @@ public class PackageParser {
return false;
}
- /**
- * @hide
- */
+ /** @hide */
+ public boolean isExternal() {
+ return applicationInfo.isExternal();
+ }
+
+ /** @hide */
public boolean isForwardLocked() {
return applicationInfo.isForwardLocked();
}
- /**
- * @hide
- */
- public boolean isSystemApp() {
- return applicationInfo.isSystemApp();
+ /** @hide */
+ public boolean isOem() {
+ return applicationInfo.isOem();
}
- /**
- * @hide
- */
- public boolean isPrivilegedApp() {
+ /** @hide */
+ public boolean isPrivileged() {
return applicationInfo.isPrivilegedApp();
}
- /**
- * @hide
- */
+ /** @hide */
+ public boolean isSystem() {
+ return applicationInfo.isSystemApp();
+ }
+
+ /** @hide */
public boolean isUpdatedSystemApp() {
return applicationInfo.isUpdatedSystemApp();
}
- /**
- * @hide
- */
+ /** @hide */
public boolean canHaveOatDir() {
// The following app types CANNOT have oat directory
// - non-updated system apps
// - forward-locked apps or apps installed in ASEC containers
- return (!isSystemApp() || isUpdatedSystemApp())
+ return (!isSystem() || isUpdatedSystemApp())
&& !isForwardLocked() && !applicationInfo.isExternalAsec();
}
public boolean isMatch(int flags) {
if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
- return isSystemApp();
+ return isSystem();
}
return true;
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 595d85715fc9..996824d4393d 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -102,7 +102,7 @@ public class UsbManager {
"android.hardware.usb.action.USB_PORT_CHANGED";
/**
- * Broadcast Action: A broadcast for USB device attached event.
+ * Activity intent sent when a USB device is attached.
*
* This intent is sent when a USB device is attached to the USB bus when in host mode.
* <ul>
@@ -128,9 +128,8 @@ public class UsbManager {
"android.hardware.usb.action.USB_DEVICE_DETACHED";
/**
- * Broadcast Action: A broadcast for USB accessory attached event.
+ * Activity intent sent when a USB accessory is attached.
*
- * This intent is sent when a USB accessory is attached.
* <ul>
* <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
* for the attached accessory
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 2468225fe62f..2c7e51a1db25 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -48,6 +48,7 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Implements the internal IInputMethod interface to convert incoming calls
@@ -76,6 +77,20 @@ class IInputMethodWrapper extends IInputMethod.Stub
final WeakReference<InputMethod> mInputMethod;
final int mTargetSdkVersion;
+ /**
+ * This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()}
+ * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been
+ * called or not, mainly to avoid unnecessary blocking operations.
+ *
+ * <p>This field must be set and cleared only from the binder thread(s), where the system
+ * guarantees that {@link #bindInput(InputBinding)},
+ * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean)}, and
+ * {@link #unbindInput()} are called with the same order as the original calls
+ * in {@link com.android.server.InputMethodManagerService}. See {@link IBinder#FLAG_ONEWAY}
+ * for detailed semantics.</p>
+ */
+ AtomicBoolean mIsUnbindIssued = null;
+
// NOTE: we should have a cache of these.
static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
final Context mContext;
@@ -163,8 +178,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
final IBinder startInputToken = (IBinder) args.arg1;
final IInputContext inputContext = (IInputContext) args.arg2;
final EditorInfo info = (EditorInfo) args.arg3;
+ final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4;
final InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null;
+ ? new InputConnectionWrapper(
+ mTarget, inputContext, missingMethods, isUnbindIssued) : null;
info.makeCompatible(mTargetSdkVersion);
inputMethod.dispatchStartInputWithToken(ic, info, restarting /* restarting */,
startInputToken);
@@ -236,10 +253,15 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void bindInput(InputBinding binding) {
+ if (mIsUnbindIssued != null) {
+ Log.e(TAG, "bindInput must be paired with unbindInput.");
+ }
+ mIsUnbindIssued = new AtomicBoolean();
// This IInputContext is guaranteed to implement all the methods.
final int missingMethodFlags = 0;
InputConnection ic = new InputConnectionWrapper(mTarget,
- IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags);
+ IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags,
+ mIsUnbindIssued);
InputBinding nu = new InputBinding(ic, binding);
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
}
@@ -247,6 +269,13 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void unbindInput() {
+ if (mIsUnbindIssued != null) {
+ // Signal the flag then forget it.
+ mIsUnbindIssued.set(true);
+ mIsUnbindIssued = null;
+ } else {
+ Log.e(TAG, "unbindInput must be paired with bindInput.");
+ }
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
}
@@ -255,8 +284,13 @@ class IInputMethodWrapper extends IInputMethod.Stub
public void startInput(IBinder startInputToken, IInputContext inputContext,
@InputConnectionInspector.MissingMethodFlags final int missingMethods,
EditorInfo attribute, boolean restarting) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_START_INPUT,
- missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute));
+ if (mIsUnbindIssued == null) {
+ Log.e(TAG, "startInput must be called after bindInput.");
+ mIsUnbindIssued = new AtomicBoolean();
+ }
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOOO(DO_START_INPUT,
+ missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute,
+ mIsUnbindIssued));
}
@BinderThread
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 4bb884405360..db12dd9724dc 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,6 +24,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.StringJoiner;
@@ -77,6 +80,31 @@ public final class NetworkCapabilities implements Parcelable {
*/
private long mNetworkCapabilities;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "NET_CAPABILITY_" }, value = {
+ NET_CAPABILITY_MMS,
+ NET_CAPABILITY_SUPL,
+ NET_CAPABILITY_DUN,
+ NET_CAPABILITY_FOTA,
+ NET_CAPABILITY_IMS,
+ NET_CAPABILITY_CBS,
+ NET_CAPABILITY_WIFI_P2P,
+ NET_CAPABILITY_IA,
+ NET_CAPABILITY_RCS,
+ NET_CAPABILITY_XCAP,
+ NET_CAPABILITY_EIMS,
+ NET_CAPABILITY_NOT_METERED,
+ NET_CAPABILITY_INTERNET,
+ NET_CAPABILITY_NOT_RESTRICTED,
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_NOT_VPN,
+ NET_CAPABILITY_VALIDATED,
+ NET_CAPABILITY_CAPTIVE_PORTAL,
+ NET_CAPABILITY_FOREGROUND,
+ })
+ public @interface NetCapability { }
+
/**
* Indicates this is a network that has the ability to reach the
* carrier's MMSC for sending and receiving MMS messages.
@@ -260,11 +288,11 @@ public final class NetworkCapabilities implements Parcelable {
* Multiple capabilities may be applied sequentially. Note that when searching
* for a network to satisfy a request, all capabilities requested must be satisfied.
*
- * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
+ * @param capability the capability to be added.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public NetworkCapabilities addCapability(int capability) {
+ public NetworkCapabilities addCapability(@NetCapability int capability) {
if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
throw new IllegalArgumentException("NetworkCapability out of range");
}
@@ -275,11 +303,11 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Removes (if found) the given capability from this {@code NetworkCapability} instance.
*
- * @param capability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
+ * @param capability the capability to be removed.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public NetworkCapabilities removeCapability(int capability) {
+ public NetworkCapabilities removeCapability(@NetCapability int capability) {
if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
throw new IllegalArgumentException("NetworkCapability out of range");
}
@@ -290,21 +318,20 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Gets all the capabilities set on this {@code NetworkCapability} instance.
*
- * @return an array of {@code NetworkCapabilities.NET_CAPABILITY_*} values
- * for this instance.
+ * @return an array of capability values for this instance.
* @hide
*/
- public int[] getCapabilities() {
+ public @NetCapability int[] getCapabilities() {
return BitUtils.unpackBits(mNetworkCapabilities);
}
/**
* Tests for the presence of a capabilitity on this instance.
*
- * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
+ * @param capability the capabilities to be tested for.
* @return {@code true} if set on this instance.
*/
- public boolean hasCapability(int capability) {
+ public boolean hasCapability(@NetCapability int capability) {
if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
return false;
}
@@ -385,6 +412,19 @@ public final class NetworkCapabilities implements Parcelable {
*/
private long mTransportTypes;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "TRANSPORT_" }, value = {
+ TRANSPORT_CELLULAR,
+ TRANSPORT_WIFI,
+ TRANSPORT_BLUETOOTH,
+ TRANSPORT_ETHERNET,
+ TRANSPORT_VPN,
+ TRANSPORT_WIFI_AWARE,
+ TRANSPORT_LOWPAN,
+ })
+ public @interface Transport { }
+
/**
* Indicates this network uses a Cellular transport.
*/
@@ -426,7 +466,7 @@ public final class NetworkCapabilities implements Parcelable {
public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
/** @hide */
- public static boolean isValidTransport(int transportType) {
+ public static boolean isValidTransport(@Transport int transportType) {
return (MIN_TRANSPORT <= transportType) && (transportType <= MAX_TRANSPORT);
}
@@ -449,11 +489,11 @@ public final class NetworkCapabilities implements Parcelable {
* to be selected. This is logically different than
* {@code NetworkCapabilities.NET_CAPABILITY_*} listed above.
*
- * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be added.
+ * @param transportType the transport type to be added.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public NetworkCapabilities addTransportType(int transportType) {
+ public NetworkCapabilities addTransportType(@Transport int transportType) {
checkValidTransportType(transportType);
mTransportTypes |= 1 << transportType;
setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
@@ -463,11 +503,11 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Removes (if found) the given transport from this {@code NetworkCapability} instance.
*
- * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be removed.
+ * @param transportType the transport type to be removed.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public NetworkCapabilities removeTransportType(int transportType) {
+ public NetworkCapabilities removeTransportType(@Transport int transportType) {
checkValidTransportType(transportType);
mTransportTypes &= ~(1 << transportType);
setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
@@ -477,21 +517,20 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Gets all the transports set on this {@code NetworkCapability} instance.
*
- * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values
- * for this instance.
+ * @return an array of transport type values for this instance.
* @hide
*/
- public int[] getTransportTypes() {
+ public @Transport int[] getTransportTypes() {
return BitUtils.unpackBits(mTransportTypes);
}
/**
* Tests for the presence of a transport on this instance.
*
- * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be tested for.
+ * @param transportType the transport type to be tested for.
* @return {@code true} if set on this instance.
*/
- public boolean hasTransport(int transportType) {
+ public boolean hasTransport(@Transport int transportType) {
return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0);
}
@@ -896,7 +935,7 @@ public final class NetworkCapabilities implements Parcelable {
/**
* @hide
*/
- public static String capabilityNamesOf(int[] capabilities) {
+ public static String capabilityNamesOf(@NetCapability int[] capabilities) {
StringJoiner joiner = new StringJoiner("|");
if (capabilities != null) {
for (int c : capabilities) {
@@ -909,7 +948,7 @@ public final class NetworkCapabilities implements Parcelable {
/**
* @hide
*/
- public static String capabilityNameOf(int capability) {
+ public static String capabilityNameOf(@NetCapability int capability) {
switch (capability) {
case NET_CAPABILITY_MMS: return "MMS";
case NET_CAPABILITY_SUPL: return "SUPL";
@@ -937,7 +976,7 @@ public final class NetworkCapabilities implements Parcelable {
/**
* @hide
*/
- public static String transportNamesOf(int[] types) {
+ public static String transportNamesOf(@Transport int[] types) {
StringJoiner joiner = new StringJoiner("|");
if (types != null) {
for (int t : types) {
@@ -950,14 +989,14 @@ public final class NetworkCapabilities implements Parcelable {
/**
* @hide
*/
- public static String transportNameOf(int transport) {
+ public static String transportNameOf(@Transport int transport) {
if (!isValidTransport(transport)) {
return "UNKNOWN";
}
return TRANSPORT_NAMES[transport];
}
- private static void checkValidTransportType(int transport) {
+ private static void checkValidTransportType(@Transport int transport) {
Preconditions.checkArgument(
isValidTransport(transport), "Invalid TransportType " + transport);
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 95a8bb472939..25b1705262e8 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -155,14 +155,13 @@ public class NetworkRequest implements Parcelable {
* Add the given capability requirement to this builder. These represent
* the requested network's required capabilities. Note that when searching
* for a network to satisfy a request, all capabilities requested must be
- * satisfied. See {@link NetworkCapabilities} for {@code NET_CAPABILITY_*}
- * definitions.
+ * satisfied.
*
- * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to add.
+ * @param capability The capability to add.
* @return The builder to facilitate chaining
* {@code builder.addCapability(...).addCapability();}.
*/
- public Builder addCapability(int capability) {
+ public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
mNetworkCapabilities.addCapability(capability);
return this;
}
@@ -170,10 +169,10 @@ public class NetworkRequest implements Parcelable {
/**
* Removes (if found) the given capability from this builder instance.
*
- * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to remove.
+ * @param capability The capability to remove.
* @return The builder to facilitate chaining.
*/
- public Builder removeCapability(int capability) {
+ public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
mNetworkCapabilities.removeCapability(capability);
return this;
}
@@ -208,13 +207,12 @@ public class NetworkRequest implements Parcelable {
* Adds the given transport requirement to this builder. These represent
* the set of allowed transports for the request. Only networks using one
* of these transports will satisfy the request. If no particular transports
- * are required, none should be specified here. See {@link NetworkCapabilities}
- * for {@code TRANSPORT_*} definitions.
+ * are required, none should be specified here.
*
- * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to add.
+ * @param transportType The transport type to add.
* @return The builder to facilitate chaining.
*/
- public Builder addTransportType(int transportType) {
+ public Builder addTransportType(@NetworkCapabilities.Transport int transportType) {
mNetworkCapabilities.addTransportType(transportType);
return this;
}
@@ -222,10 +220,10 @@ public class NetworkRequest implements Parcelable {
/**
* Removes (if found) the given transport from this builder instance.
*
- * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to remove.
+ * @param transportType The transport type to remove.
* @return The builder to facilitate chaining.
*/
- public Builder removeTransportType(int transportType) {
+ public Builder removeTransportType(@NetworkCapabilities.Transport int transportType) {
mNetworkCapabilities.removeTransportType(transportType);
return this;
}
diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java
index 30b2656227d0..2495cab1adf9 100644
--- a/core/java/android/net/metrics/ConnectStats.java
+++ b/core/java/android/net/metrics/ConnectStats.java
@@ -20,6 +20,7 @@ import android.net.NetworkCapabilities;
import android.system.OsConstants;
import android.util.IntArray;
import android.util.SparseIntArray;
+
import com.android.internal.util.BitUtils;
import com.android.internal.util.TokenBucket;
@@ -43,6 +44,8 @@ public class ConnectStats {
public final TokenBucket mLatencyTb;
/** Maximum number of latency values recorded. */
public final int mMaxLatencyRecords;
+ /** Total count of events */
+ public int eventCount = 0;
/** Total count of successful connects. */
public int connectCount = 0;
/** Total count of successful connects done in blocking mode. */
@@ -57,12 +60,15 @@ public class ConnectStats {
mMaxLatencyRecords = maxLatencyRecords;
}
- public void addEvent(int errno, int latencyMs, String ipAddr) {
+ boolean addEvent(int errno, int latencyMs, String ipAddr) {
+ eventCount++;
if (isSuccess(errno)) {
countConnect(errno, ipAddr);
countLatency(errno, latencyMs);
+ return true;
} else {
countError(errno);
+ return false;
}
}
@@ -101,7 +107,7 @@ public class ConnectStats {
return (errno == 0) || isNonBlocking(errno);
}
- private static boolean isNonBlocking(int errno) {
+ static boolean isNonBlocking(int errno) {
// On non-blocking TCP sockets, connect() immediately returns EINPROGRESS.
// On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY.
return (errno == EINPROGRESS) || (errno == EALREADY);
@@ -117,6 +123,7 @@ public class ConnectStats {
for (int t : BitUtils.unpackBits(transports)) {
builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
}
+ builder.append(String.format("%d events, ", eventCount));
builder.append(String.format("%d success, ", connectCount));
builder.append(String.format("%d blocking, ", connectBlockingCount));
builder.append(String.format("%d IPv6 dst", ipv6ConnectCount));
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index 28cf42f2fe28..eb61c1532d49 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -16,73 +16,43 @@
package android.net.metrics;
+import static android.net.ConnectivityManager.NETID_UNSET;
+
import android.net.NetworkCapabilities;
-import android.os.Parcel;
-import android.os.Parcelable;
/**
* An event recorded by ConnectivityService when there is a change in the default network.
* {@hide}
*/
-public final class DefaultNetworkEvent implements Parcelable {
+public class DefaultNetworkEvent {
+
// The ID of the network that has become the new default or NETID_UNSET if none.
- public final int netId;
+ public int netId = NETID_UNSET;
// The list of transport types of the new default network, for example TRANSPORT_WIFI, as
// defined in NetworkCapabilities.java.
- public final int[] transportTypes;
+ public int[] transportTypes = new int[0];
// The ID of the network that was the default before or NETID_UNSET if none.
- public final int prevNetId;
+ public int prevNetId = NETID_UNSET;
// Whether the previous network had IPv4/IPv6 connectivity.
- public final boolean prevIPv4;
- public final boolean prevIPv6;
-
- public DefaultNetworkEvent(int netId, int[] transportTypes,
- int prevNetId, boolean prevIPv4, boolean prevIPv6) {
- this.netId = netId;
- this.transportTypes = transportTypes;
- this.prevNetId = prevNetId;
- this.prevIPv4 = prevIPv4;
- this.prevIPv6 = prevIPv6;
- }
-
- private DefaultNetworkEvent(Parcel in) {
- this.netId = in.readInt();
- this.transportTypes = in.createIntArray();
- this.prevNetId = in.readInt();
- this.prevIPv4 = (in.readByte() > 0);
- this.prevIPv6 = (in.readByte() > 0);
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(netId);
- out.writeIntArray(transportTypes);
- out.writeInt(prevNetId);
- out.writeByte(prevIPv4 ? (byte) 1 : (byte) 0);
- out.writeByte(prevIPv6 ? (byte) 1 : (byte) 0);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
+ public boolean prevIPv4;
+ public boolean prevIPv6;
@Override
public String toString() {
- String prevNetwork = String.valueOf(prevNetId);
- String newNetwork = String.valueOf(netId);
- if (prevNetId != 0) {
- prevNetwork += ":" + ipSupport();
- }
- if (netId != 0) {
- newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes);
- }
- return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork);
+ String prevNetwork = String.valueOf(prevNetId);
+ String newNetwork = String.valueOf(netId);
+ if (prevNetId != 0) {
+ prevNetwork += ":" + ipSupport();
+ }
+ if (netId != 0) {
+ newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes);
+ }
+ return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork);
}
private String ipSupport() {
if (prevIPv4 && prevIPv6) {
- return "DUAL";
+ return "IPv4v6";
}
if (prevIPv6) {
return "IPv6";
@@ -92,15 +62,4 @@ public final class DefaultNetworkEvent implements Parcelable {
}
return "NONE";
}
-
- public static final Parcelable.Creator<DefaultNetworkEvent> CREATOR
- = new Parcelable.Creator<DefaultNetworkEvent>() {
- public DefaultNetworkEvent createFromParcel(Parcel in) {
- return new DefaultNetworkEvent(in);
- }
-
- public DefaultNetworkEvent[] newArray(int size) {
- return new DefaultNetworkEvent[size];
- }
- };
}
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index a4970e4d0d28..81b098bbb38e 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -17,11 +17,13 @@
package android.net.metrics;
import android.net.NetworkCapabilities;
-import java.util.Arrays;
+
import com.android.internal.util.BitUtils;
+import java.util.Arrays;
+
/**
- * A DNS event recorded by NetdEventListenerService.
+ * A batch of DNS events recorded by NetdEventListenerService for a specific network.
* {@hide}
*/
final public class DnsEvent {
@@ -38,6 +40,8 @@ final public class DnsEvent {
// the eventTypes, returnCodes, and latenciesMs arrays have the same length and the i-th event
// is spread across the three array at position i.
public int eventCount;
+ // The number of successful DNS queries recorded.
+ public int successCount;
// The types of DNS queries as defined in INetdEventListener.
public byte[] eventTypes;
// Current getaddrinfo codes go from 1 to EAI_MAX = 15. gethostbyname returns errno, but there
@@ -54,10 +58,11 @@ final public class DnsEvent {
latenciesMs = new int[initialCapacity];
}
- public void addResult(byte eventType, byte returnCode, int latencyMs) {
+ boolean addResult(byte eventType, byte returnCode, int latencyMs) {
+ boolean isSuccess = (returnCode == 0);
if (eventCount >= SIZE_LIMIT) {
// TODO: implement better rate limiting that does not biases metrics.
- return;
+ return isSuccess;
}
if (eventCount == eventTypes.length) {
resize((int) (1.4 * eventCount));
@@ -66,6 +71,10 @@ final public class DnsEvent {
returnCodes[eventCount] = returnCode;
latenciesMs[eventCount] = latencyMs;
eventCount++;
+ if (isSuccess) {
+ successCount++;
+ }
+ return isSuccess;
}
public void resize(int newLength) {
@@ -80,6 +89,8 @@ final public class DnsEvent {
for (int t : BitUtils.unpackBits(transports)) {
builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
}
- return builder.append(eventCount).append(" events)").toString();
+ builder.append(String.format("%d events, ", eventCount));
+ builder.append(String.format("%d success)", successCount));
+ return builder.toString();
}
}
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
new file mode 100644
index 000000000000..2b662a0c28e2
--- /dev/null
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -0,0 +1,168 @@
+/*
+ * 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.metrics;
+
+import android.net.NetworkCapabilities;
+
+import com.android.internal.util.BitUtils;
+import com.android.internal.util.TokenBucket;
+
+import java.util.StringJoiner;
+
+/**
+ * A class accumulating network metrics received from Netd regarding dns queries and
+ * connect() calls on a given network.
+ *
+ * This class also accumulates running sums of dns and connect latency stats and
+ * error counts for bug report logging.
+ *
+ * @hide
+ */
+public class NetworkMetrics {
+
+ private static final int INITIAL_DNS_BATCH_SIZE = 100;
+ private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
+
+ // The network id of the Android Network.
+ public final int netId;
+ // The transport types bitmap of the Android Network, as defined in NetworkCapabilities.java.
+ public final long transports;
+ // Accumulated metrics for connect events.
+ public final ConnectStats connectMetrics;
+ // Accumulated metrics for dns events.
+ public final DnsEvent dnsMetrics;
+ // Running sums of latencies and error counts for connect and dns events.
+ public final Summary summary;
+ // Running sums of the most recent latencies and error counts for connect and dns events.
+ // Starts null until some events are accumulated.
+ // Allows to collect periodic snapshot of the running summaries for a given network.
+ public Summary pendingSummary;
+
+ public NetworkMetrics(int netId, long transports, TokenBucket tb) {
+ this.netId = netId;
+ this.transports = transports;
+ this.connectMetrics =
+ new ConnectStats(netId, transports, tb, CONNECT_LATENCY_MAXIMUM_RECORDS);
+ this.dnsMetrics = new DnsEvent(netId, transports, INITIAL_DNS_BATCH_SIZE);
+ this.summary = new Summary(netId, transports);
+ }
+
+ /**
+ * Get currently pending Summary statistics, if any, for this NetworkMetrics, merge them
+ * into the long running Summary statistics of this NetworkMetrics, and also clear them.
+ */
+ public Summary getPendingStats() {
+ Summary s = pendingSummary;
+ pendingSummary = null;
+ if (s != null) {
+ summary.merge(s);
+ }
+ return s;
+ }
+
+ /** Accumulate a dns query result reported by netd. */
+ public void addDnsResult(int eventType, int returnCode, int latencyMs) {
+ if (pendingSummary == null) {
+ pendingSummary = new Summary(netId, transports);
+ }
+ boolean isSuccess = dnsMetrics.addResult((byte) eventType, (byte) returnCode, latencyMs);
+ pendingSummary.dnsLatencies.count(latencyMs);
+ pendingSummary.dnsErrorRate.count(isSuccess ? 0 : 1);
+ }
+
+ /** Accumulate a connect query result reported by netd. */
+ public void addConnectResult(int error, int latencyMs, String ipAddr) {
+ if (pendingSummary == null) {
+ pendingSummary = new Summary(netId, transports);
+ }
+ boolean isSuccess = connectMetrics.addEvent(error, latencyMs, ipAddr);
+ pendingSummary.connectErrorRate.count(isSuccess ? 0 : 1);
+ if (ConnectStats.isNonBlocking(error)) {
+ pendingSummary.connectLatencies.count(latencyMs);
+ }
+ }
+
+ /** Represents running sums for dns and connect average error counts and average latencies. */
+ public static class Summary {
+
+ public final int netId;
+ public final long transports;
+ // DNS latencies measured in milliseconds.
+ public final Metrics dnsLatencies = new Metrics();
+ // DNS error rate measured in percentage points.
+ public final Metrics dnsErrorRate = new Metrics();
+ // Blocking connect latencies measured in milliseconds.
+ public final Metrics connectLatencies = new Metrics();
+ // Blocking and non blocking connect error rate measured in percentage points.
+ public final Metrics connectErrorRate = new Metrics();
+
+ public Summary(int netId, long transports) {
+ this.netId = netId;
+ this.transports = transports;
+ }
+
+ void merge(Summary that) {
+ dnsLatencies.merge(that.dnsLatencies);
+ dnsErrorRate.merge(that.dnsErrorRate);
+ connectLatencies.merge(that.connectLatencies);
+ connectErrorRate.merge(that.connectErrorRate);
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner j = new StringJoiner(", ", "{", "}");
+ j.add("netId=" + netId);
+ for (int t : BitUtils.unpackBits(transports)) {
+ j.add(NetworkCapabilities.transportNameOf(t));
+ }
+ j.add(String.format("dns avg=%dms max=%dms err=%.1f%% tot=%d",
+ (int) dnsLatencies.average(), (int) dnsLatencies.max,
+ 100 * dnsErrorRate.average(), dnsErrorRate.count));
+ j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d",
+ (int) connectLatencies.average(), (int) connectLatencies.max,
+ 100 * connectErrorRate.average(), connectErrorRate.count));
+ return j.toString();
+ }
+ }
+
+ /** Tracks a running sum and returns the average of a metric. */
+ static class Metrics {
+ public double sum;
+ public double max = Double.MIN_VALUE;
+ public int count;
+
+ void merge(Metrics that) {
+ this.count += that.count;
+ this.sum += that.sum;
+ this.max = Math.max(this.max, that.max);
+ }
+
+ void count(double value) {
+ count++;
+ sum += value;
+ max = Math.max(max, value);
+ }
+
+ double average() {
+ double a = sum / (double) count;
+ if (Double.isNaN(a)) {
+ a = 0;
+ }
+ return a;
+ }
+ }
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8682c01ebd85..a8bd940326d6 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1986,7 +1986,7 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getDeviceIdlingTime(int mode, long elapsedRealtimeUs, int which);
/**
- * Returns the number of times that the devie has started idling.
+ * Returns the number of times that the device has started idling.
*
* {@hide}
*/
@@ -6453,7 +6453,7 @@ public abstract class BatteryStats implements Parcelable {
pw.println();
}
}
- if (!filtering || (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0) {
+ if (!filtering || (flags & DUMP_DAILY_ONLY) != 0) {
pw.println("Daily stats:");
pw.print(" Current start time: ");
pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index e9e695bbdf10..66e1651997b4 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -167,7 +167,7 @@ public class Binder implements IBinder {
try {
if (binder instanceof BinderProxy) {
((BinderProxy) binder).mWarnOnBlocking = false;
- } else if (binder != null
+ } else if (binder != null && binder.getInterfaceDescriptor() != null
&& binder.queryLocalInterface(binder.getInterfaceDescriptor()) == null) {
Log.w(TAG, "Unable to allow blocking on interface " + binder);
}
@@ -414,7 +414,7 @@ public class Binder implements IBinder {
* descriptor.
*/
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
- if (mDescriptor.equals(descriptor)) {
+ if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
diff --git a/core/java/android/view/autofill/AutoFillType.aidl b/core/java/android/os/IStatsCallbacks.aidl
index 4606b48e9e10..02e7cd3978c8 100644
--- a/core/java/android/view/autofill/AutoFillType.aidl
+++ b/core/java/android/os/IStatsCallbacks.aidl
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package android.view.autofill;
+package android.os;
-/*
- * TODO(b/35956626): remove once clients use getAutoFilltype()
- */
-parcelable AutoFillType; \ No newline at end of file
+/**
+ * Callback for Statsd to allow binder calls to clients.
+ * {@hide}
+ */
+interface IStatsCallbacks {
+ void onReceiveLogs(out byte[] log);
+}
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index daacc4e832f9..480296c12e50 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -16,6 +16,8 @@
package android.os;
+import android.os.IStatsCallbacks;
+
/**
* Binder interface to communicate with the statistics management service.
* {@hide}
@@ -61,4 +63,15 @@ interface IStatsManager {
* Inform stats that an app was removed.
*/
oneway void informOnePackageRemoved(in String app, in int uid);
+
+ /**
+ * Trigger pushLog to force push stats log entries from statsd on client side.
+ */
+ void requestPush();
+
+ /**
+ * Listen to statsd to send stats log entries.
+ * TODO: Limit callbacks with specific configurations.
+ */
+ void subscribeStatsLog(IStatsCallbacks callbacks);
}
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index e4a12e84e466..6223235e628f 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -17,6 +17,7 @@
package android.os;
import android.util.Slog;
+
import com.android.internal.util.FastPrintWriter;
import java.io.BufferedInputStream;
@@ -118,13 +119,33 @@ public abstract class ShellCommand {
mErrPrintWriter.flush();
}
if (DEBUG) Slog.d(TAG, "Sending command result on " + mTarget);
- mResultReceiver.send(res, null);
+ if (mResultReceiver != null) {
+ mResultReceiver.send(res, null);
+ }
}
if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget);
return res;
}
/**
+ * Adopt the ResultReceiver that was given to this shell command from it, taking
+ * it over. Primarily used to dispatch to another shell command. Once called,
+ * this shell command will no longer return its own result when done.
+ */
+ public ResultReceiver adoptResultReceiver() {
+ ResultReceiver rr = mResultReceiver;
+ mResultReceiver = null;
+ return rr;
+ }
+
+ /**
+ * Return the raw FileDescriptor for the output stream.
+ */
+ public FileDescriptor getOutFileDescriptor() {
+ return mOut;
+ }
+
+ /**
* Return direct raw access (not buffered) to the command's output data stream.
*/
public OutputStream getRawOutputStream() {
@@ -145,6 +166,13 @@ public abstract class ShellCommand {
}
/**
+ * Return the raw FileDescriptor for the error stream.
+ */
+ public FileDescriptor getErrFileDescriptor() {
+ return mErr;
+ }
+
+ /**
* Return direct raw access (not buffered) to the command's error output data stream.
*/
public OutputStream getRawErrorStream() {
@@ -168,6 +196,13 @@ public abstract class ShellCommand {
}
/**
+ * Return the raw FileDescriptor for the input stream.
+ */
+ public FileDescriptor getInFileDescriptor() {
+ return mIn;
+ }
+
+ /**
* Return direct raw access (not buffered) to the command's input data stream.
*/
public InputStream getRawInputStream() {
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 826ec1eb129e..ee3e5bc9a001 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -20,7 +20,6 @@ import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
-import android.app.ApplicationErrorReport;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -35,6 +34,7 @@ import android.util.Singleton;
import android.util.Slog;
import android.view.IWindowManager;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.RuntimeInit;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
@@ -48,8 +48,10 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Deque;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -352,8 +354,8 @@ public final class StrictMode {
} else {
msg = "StrictMode policy violation:";
}
- if (info.crashInfo != null) {
- Log.d(TAG, msg + " " + info.crashInfo.stackTrace);
+ if (info.hasStackTrace()) {
+ Log.d(TAG, msg + " " + info.getStackTrace());
} else {
Log.d(TAG, msg + " missing stack trace!");
}
@@ -1247,28 +1249,6 @@ public final class StrictMode {
}
}
- /** Like parsePolicyFromMessage(), but returns the violation. */
- private static int parseViolationFromMessage(String message) {
- if (message == null) {
- return 0;
- }
- int violationIndex = message.indexOf("violation=");
- if (violationIndex == -1) {
- return 0;
- }
- int numberStartIndex = violationIndex + "violation=".length();
- int numberEndIndex = message.indexOf(' ', numberStartIndex);
- if (numberEndIndex == -1) {
- numberEndIndex = message.length();
- }
- String violationString = message.substring(numberStartIndex, numberEndIndex);
- try {
- return Integer.parseInt(violationString);
- } catch (NumberFormatException e) {
- return 0;
- }
- }
-
private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed =
new ThreadLocal<ArrayList<ViolationInfo>>() {
@Override
@@ -1516,7 +1496,7 @@ public final class StrictMode {
// to people who push/pop temporary policy in regions of code,
// hence the policy being passed around.
void handleViolation(final ViolationInfo info) {
- if (info == null || info.crashInfo == null || info.crashInfo.stackTrace == null) {
+ if (info == null || !info.hasStackTrace()) {
Log.wtf(TAG, "unexpected null stacktrace");
return;
}
@@ -1530,7 +1510,7 @@ public final class StrictMode {
gatheredViolations.set(violations);
}
for (ViolationInfo previous : violations) {
- if (info.crashInfo.stackTrace.equals(previous.crashInfo.stackTrace)) {
+ if (info.getStackTrace().equals(previous.getStackTrace())) {
// Duplicate. Don't log.
return;
}
@@ -1576,8 +1556,7 @@ public final class StrictMode {
}
if (violationMaskSubset != 0) {
- int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
- violationMaskSubset |= violationBit;
+ violationMaskSubset |= info.getViolationBit();
final int savedPolicyMask = getThreadPolicyMask();
final boolean justDropBox = (info.policy & THREAD_PENALTY_MASK) == PENALTY_DROPBOX;
@@ -1622,8 +1601,7 @@ public final class StrictMode {
}
private static void executeDeathPenalty(ViolationInfo info) {
- int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
- throw new StrictModeViolation(info.policy, violationBit, null);
+ throw new StrictModeViolation(info.policy, info.getViolationBit(), null);
}
/**
@@ -1670,7 +1648,7 @@ public final class StrictMode {
private static class AndroidCloseGuardReporter implements CloseGuard.Reporter {
public void report(String message, Throwable allocationSite) {
- onVmPolicyViolation(message, allocationSite);
+ onVmPolicyViolation(allocationSite);
}
}
@@ -1709,7 +1687,7 @@ public final class StrictMode {
long instances = instanceCounts[i];
if (instances > limit) {
Throwable tr = new InstanceCountViolation(klass, instances, limit);
- onVmPolicyViolation(tr.getMessage(), tr);
+ onVmPolicyViolation(tr);
}
}
}
@@ -1833,22 +1811,24 @@ public final class StrictMode {
/** @hide */
public static void onSqliteObjectLeaked(String message, Throwable originStack) {
- onVmPolicyViolation(message, originStack);
+ Throwable t = new Throwable(message);
+ t.setStackTrace(originStack.getStackTrace());
+ onVmPolicyViolation(t);
}
/** @hide */
public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
- onVmPolicyViolation(null, originStack);
+ onVmPolicyViolation(originStack);
}
/** @hide */
public static void onIntentReceiverLeaked(Throwable originStack) {
- onVmPolicyViolation(null, originStack);
+ onVmPolicyViolation(originStack);
}
/** @hide */
public static void onServiceConnectionLeaked(Throwable originStack) {
- onVmPolicyViolation(null, originStack);
+ onVmPolicyViolation(originStack);
}
/** @hide */
@@ -1857,7 +1837,7 @@ public final class StrictMode {
if ((sVmPolicy.mask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) {
throw new FileUriExposedException(message);
} else {
- onVmPolicyViolation(null, new Throwable(message));
+ onVmPolicyViolation(new Throwable(message));
}
}
@@ -1869,7 +1849,7 @@ public final class StrictMode {
+ location
+ " without permission grant flags; did you forget"
+ " FLAG_GRANT_READ_URI_PERMISSION?";
- onVmPolicyViolation(null, new Throwable(message));
+ onVmPolicyViolation(new Throwable(message));
}
/** @hide */
@@ -1899,10 +1879,9 @@ public final class StrictMode {
} catch (UnknownHostException ignored) {
}
}
-
+ msg += HexDump.dumpHexString(firstPacket).trim() + " ";
final boolean forceDeath = (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0;
- onVmPolicyViolation(
- HexDump.dumpHexString(firstPacket).trim(), new Throwable(msg), forceDeath);
+ onVmPolicyViolation(new Throwable(msg), forceDeath);
}
/** @hide */
@@ -1912,24 +1891,23 @@ public final class StrictMode {
/** @hide */
public static void onUntaggedSocket() {
- onVmPolicyViolation(null, new Throwable(UNTAGGED_SOCKET_VIOLATION_MESSAGE));
+ onVmPolicyViolation(new Throwable(UNTAGGED_SOCKET_VIOLATION_MESSAGE));
}
// Map from VM violation fingerprint to uptime millis.
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
/** @hide */
- public static void onVmPolicyViolation(String message, Throwable originStack) {
- onVmPolicyViolation(message, originStack, false);
+ public static void onVmPolicyViolation(Throwable originStack) {
+ onVmPolicyViolation(originStack, false);
}
/** @hide */
- public static void onVmPolicyViolation(
- String message, Throwable originStack, boolean forceDeath) {
+ public static void onVmPolicyViolation(Throwable originStack, boolean forceDeath) {
final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
- final ViolationInfo info = new ViolationInfo(message, originStack, sVmPolicy.mask);
+ final ViolationInfo info = new ViolationInfo(originStack, sVmPolicy.mask);
// Erase stuff not relevant for process-wide violations
info.numAnimationsRunning = 0;
@@ -2027,21 +2005,14 @@ public final class StrictMode {
* read back all the encoded violations.
*/
/* package */ static void readAndHandleBinderCallViolations(Parcel p) {
- // Our own stack trace to append
- StringWriter sw = new StringWriter();
- sw.append("# via Binder call with stack:\n");
- PrintWriter pw = new FastPrintWriter(sw, false, 256);
- new LogStackTrace().printStackTrace(pw);
- pw.flush();
- String ourStack = sw.toString();
-
+ LogStackTrace localCallSite = new LogStackTrace();
final int policyMask = getThreadPolicyMask();
final boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0;
final int size = p.readInt();
for (int i = 0; i < size; i++) {
final ViolationInfo info = new ViolationInfo(p, !currentlyGathering);
- info.crashInfo.appendStackTrace(ourStack);
+ info.addLocalStack(localCallSite);
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (policy instanceof AndroidBlockGuardPolicy) {
((AndroidBlockGuardPolicy) policy).handleViolationWithTimingAttempt(info);
@@ -2254,7 +2225,7 @@ public final class StrictMode {
// StrictMode not enabled.
return;
}
- ((AndroidBlockGuardPolicy) policy).onUnbufferedIO();
+ policy.onUnbufferedIO();
}
/** @hide */
@@ -2264,7 +2235,7 @@ public final class StrictMode {
// StrictMode not enabled.
return;
}
- ((AndroidBlockGuardPolicy) policy).onReadFromDisk();
+ policy.onReadFromDisk();
}
/** @hide */
@@ -2274,12 +2245,11 @@ public final class StrictMode {
// StrictMode not enabled.
return;
}
- ((AndroidBlockGuardPolicy) policy).onWriteToDisk();
+ policy.onWriteToDisk();
}
- // Guarded by StrictMode.class
- private static final HashMap<Class, Integer> sExpectedActivityInstanceCount =
- new HashMap<Class, Integer>();
+ @GuardedBy("StrictMode.class")
+ private static final HashMap<Class, Integer> sExpectedActivityInstanceCount = new HashMap<>();
/**
* Returns an object that is used to track instances of activites. The activity should store a
@@ -2354,7 +2324,7 @@ public final class StrictMode {
long instances = VMDebug.countInstancesOfClass(klass, false);
if (instances > limit) {
Throwable tr = new InstanceCountViolation(klass, instances, limit);
- onVmPolicyViolation(tr.getMessage(), tr);
+ onVmPolicyViolation(tr);
}
}
@@ -2366,10 +2336,15 @@ public final class StrictMode {
*/
@TestApi
public static final class ViolationInfo implements Parcelable {
- public final String message;
+ /** Stack and violation details. */
+ @Nullable private final Throwable mThrowable;
+
+ private final Deque<Throwable> mBinderStack = new ArrayDeque<>();
- /** Stack and other stuff info. */
- @Nullable public final ApplicationErrorReport.CrashInfo crashInfo;
+ /** Memoized stack trace of full violation. */
+ @Nullable private String mStackTrace;
+ /** Memoized violation bit. */
+ private int mViolationBit;
/** The strict mode policy mask at the time of violation. */
public final int policy;
@@ -2404,19 +2379,13 @@ public final class StrictMode {
/** Create an uninitialized instance of ViolationInfo */
public ViolationInfo() {
- message = null;
- crashInfo = null;
+ mThrowable = null;
policy = 0;
}
- public ViolationInfo(Throwable tr, int policy) {
- this(null, tr, policy);
- }
-
/** Create an instance of ViolationInfo initialized from an exception. */
- public ViolationInfo(String message, Throwable tr, int policy) {
- this.message = message;
- crashInfo = new ApplicationErrorReport.CrashInfo(tr);
+ public ViolationInfo(Throwable tr, int policy) {
+ this.mThrowable = tr;
violationUptimeMillis = SystemClock.uptimeMillis();
this.policy = policy;
this.numAnimationsRunning = ValueAnimator.getCurrentAnimationsCount();
@@ -2446,11 +2415,91 @@ public final class StrictMode {
}
}
+ /** Equivalent output to {@link ApplicationErrorReport.CrashInfo#stackTrace}. */
+ public String getStackTrace() {
+ if (mThrowable != null && mStackTrace == null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new FastPrintWriter(sw, false, 256);
+ mThrowable.printStackTrace(pw);
+ for (Throwable t : mBinderStack) {
+ pw.append("# via Binder call with stack:\n");
+ t.printStackTrace(pw);
+ }
+ pw.flush();
+ pw.close();
+ mStackTrace = sw.toString();
+ }
+ return mStackTrace;
+ }
+
+ /**
+ * Optional message describing this violation.
+ *
+ * @hide
+ */
+ @TestApi
+ public String getViolationDetails() {
+ if (mThrowable != null) {
+ return mThrowable.getMessage();
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * If this violation has a useful stack trace.
+ *
+ * @hide
+ */
+ public boolean hasStackTrace() {
+ return mThrowable != null;
+ }
+
+ /**
+ * Add a {@link Throwable} from the current process that caused the underlying violation.
+ *
+ * @hide
+ */
+ void addLocalStack(Throwable t) {
+ mBinderStack.addFirst(t);
+ }
+
+ /**
+ * Retrieve the type of StrictMode violation.
+ *
+ * @hide
+ */
+ int getViolationBit() {
+ if (mThrowable == null || mThrowable.getMessage() == null) {
+ return 0;
+ }
+ if (mViolationBit != 0) {
+ return mViolationBit;
+ }
+ String message = mThrowable.getMessage();
+ int violationIndex = message.indexOf("violation=");
+ if (violationIndex == -1) {
+ return 0;
+ }
+ int numberStartIndex = violationIndex + "violation=".length();
+ int numberEndIndex = message.indexOf(' ', numberStartIndex);
+ if (numberEndIndex == -1) {
+ numberEndIndex = message.length();
+ }
+ String violationString = message.substring(numberStartIndex, numberEndIndex);
+ try {
+ mViolationBit = Integer.parseInt(violationString);
+ return mViolationBit;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
@Override
public int hashCode() {
int result = 17;
- if (crashInfo != null) {
- result = 37 * result + crashInfo.stackTrace.hashCode();
+ if (mThrowable != null) {
+ result = 37 * result + mThrowable.hashCode();
}
if (numAnimationsRunning != 0) {
result *= 37;
@@ -2478,11 +2527,10 @@ public final class StrictMode {
* should be removed.
*/
public ViolationInfo(Parcel in, boolean unsetGatheringBit) {
- message = in.readString();
- if (in.readInt() != 0) {
- crashInfo = new ApplicationErrorReport.CrashInfo(in);
- } else {
- crashInfo = null;
+ mThrowable = (Throwable) in.readSerializable();
+ int binderStackSize = in.readInt();
+ for (int i = 0; i < binderStackSize; i++) {
+ mBinderStack.add((Throwable) in.readSerializable());
}
int rawPolicy = in.readInt();
if (unsetGatheringBit) {
@@ -2502,12 +2550,10 @@ public final class StrictMode {
/** Save a ViolationInfo instance to a parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(message);
- if (crashInfo != null) {
- dest.writeInt(1);
- crashInfo.writeToParcel(dest, flags);
- } else {
- dest.writeInt(0);
+ dest.writeSerializable(mThrowable);
+ dest.writeInt(mBinderStack.size());
+ for (Throwable t : mBinderStack) {
+ dest.writeSerializable(t);
}
int start = dest.dataPosition();
dest.writeInt(policy);
@@ -2542,8 +2588,8 @@ public final class StrictMode {
/** Dump a ViolationInfo instance to a Printer. */
public void dump(Printer pw, String prefix) {
- if (crashInfo != null) {
- crashInfo.dump(pw, prefix);
+ if (mThrowable != null) {
+ pw.println(prefix + "stackTrace: " + getStackTrace());
}
pw.println(prefix + "policy: " + policy);
if (durationMillis != -1) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c200ae742125..a27df3a70259 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7615,6 +7615,13 @@ public final class Settings {
public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
/**
+ * An integer representing the Bluetooth Class of Device (CoD).
+ *
+ * @hide
+ */
+ public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
+
+ /**
* A Long representing a bitmap of profiles that should be disabled when bluetooth starts.
* See {@link android.bluetooth.BluetoothProfile}.
* {@hide}
@@ -9649,7 +9656,7 @@ public final class Settings {
* Get the key that retrieves a bluetooth Input Device's priority.
* @hide
*/
- public static final String getBluetoothInputDevicePriorityKey(String address) {
+ public static final String getBluetoothHidHostPriorityKey(String address) {
return BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
}
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 812c956f4c61..0c4eedab2fc0 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -16,7 +16,6 @@
package android.security;
-import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.security.net.config.ApplicationConfig;
@@ -63,7 +62,8 @@ public class NetworkSecurityPolicy {
* traffic from applications is handled by higher-level network stacks/components which can
* honor this aspect of the policy.
*
- * <p>NOTE: {@link android.webkit.WebView} does not honor this flag.
+ * <p>NOTE: {@link android.webkit.WebView} honors this flag for applications targeting API level
+ * 26 and up.
*/
public boolean isCleartextTrafficPermitted() {
return libcore.net.NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted();
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index b9e550540217..52f48ef8499b 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -164,7 +164,8 @@ public final class NetworkSecurityConfig {
* <p>
* The default configuration has the following properties:
* <ol>
- * <li>Cleartext traffic is permitted for non-ephemeral apps.</li>
+ * <li>If the application targets API level 27 (Android O MR1) or lower then cleartext traffic
+ * is allowed by default.</li>
* <li>Cleartext traffic is not permitted for ephemeral apps.</li>
* <li>HSTS is not enforced.</li>
* <li>No certificate pinning is used.</li>
@@ -183,7 +184,8 @@ public final class NetworkSecurityConfig {
// System certificate store, does not bypass static pins.
.addCertificatesEntryRef(
new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
- final boolean cleartextTrafficPermitted = info.targetSandboxVersion < 2;
+ final boolean cleartextTrafficPermitted = info.targetSdkVersion < Build.VERSION_CODES.P
+ && info.targetSandboxVersion < 2;
builder.setCleartextTrafficPermitted(cleartextTrafficPermitted);
// Applications targeting N and above must opt in into trusting the user added certificate
// store.
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index ac5c2e926874..4d2a9629c83a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1910,7 +1910,7 @@ public abstract class Layout {
MeasuredText mt = MeasuredText.obtain();
TextLine tl = TextLine.obtain();
try {
- mt.setPara(text, start, end, textDir, null);
+ mt.setPara(text, start, end, textDir);
Directions directions;
int dir;
if (mt.mEasy) {
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index ffc44a72c357..3d9fba71db74 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -39,7 +39,6 @@ class MeasuredText {
private int mPos;
private TextPaint mWorkPaint;
- private StaticLayout.Builder mBuilder;
private MeasuredText() {
mWorkPaint = new TextPaint();
@@ -82,7 +81,6 @@ class MeasuredText {
void finish() {
mText = null;
- mBuilder = null;
if (mLen > 1000) {
mWidths = null;
mChars = null;
@@ -93,9 +91,7 @@ class MeasuredText {
/**
* Analyzes text for bidirectional runs. Allocates working buffers.
*/
- void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir,
- StaticLayout.Builder builder) {
- mBuilder = builder;
+ void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
mText = text;
mTextStart = start;
@@ -159,12 +155,12 @@ class MeasuredText {
/**
* Apply the style.
*
- * If StaticLyaout.Builder is not provided in setPara() method, this method measures the styled
- * text width.
- * If StaticLayout.Builder is provided in setPara() method, this method just passes the style
- * information to native code by calling StaticLayout.Builder.addstyleRun() and returns 0.
+ * If nativeStaticLayoutPtr is 0, this method measures the styled text width.
+ * If nativeStaticLayoutPtr is not 0, this method just passes the style information to native
+ * code by calling StaticLayout.addstyleRun() and returns 0.
*/
- float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
+ float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm,
+ long nativeStaticLayoutPtr) {
if (fm != null) {
paint.getFontMetricsInt(fm);
}
@@ -174,10 +170,10 @@ class MeasuredText {
if (mEasy) {
final boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT;
- if (mBuilder == null) {
+ if (nativeStaticLayoutPtr == 0) {
return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p);
} else {
- mBuilder.addStyleRun(paint, p, p + len, isRtl);
+ StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, p, p + len, isRtl);
return 0.0f; // Builder.addStyleRun doesn't return the width.
}
}
@@ -187,12 +183,12 @@ class MeasuredText {
for (int q = p, i = p + 1, e = p + len;; ++i) {
if (i == e || mLevels[i] != level) {
final boolean isRtl = (level & 0x1) != 0;
- if (mBuilder == null) {
+ if (nativeStaticLayoutPtr == 0) {
totalAdvance +=
paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q);
} else {
// Builder.addStyleRun doesn't return the width.
- mBuilder.addStyleRun(paint, q, i, isRtl);
+ StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, q, i, isRtl);
}
if (i == e) {
break;
@@ -201,11 +197,15 @@ class MeasuredText {
level = mLevels[i];
}
}
- return totalAdvance; // If mBuilder is null, the result is zero.
+ return totalAdvance; // If nativeStaticLayoutPtr is 0, the result is zero.
+ }
+
+ float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
+ return addStyleRun(paint, len, fm, 0 /* native ptr */);
}
float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
- Paint.FontMetricsInt fm) {
+ Paint.FontMetricsInt fm, long nativeStaticLayoutPtr) {
TextPaint workPaint = mWorkPaint;
workPaint.set(paint);
@@ -224,18 +224,18 @@ class MeasuredText {
float wid;
if (replacement == null) {
- wid = addStyleRun(workPaint, len, fm);
+ wid = addStyleRun(workPaint, len, fm, nativeStaticLayoutPtr);
} else {
// Use original text. Shouldn't matter.
wid = replacement.getSize(workPaint, mText, mTextStart + mPos,
mTextStart + mPos + len, fm);
- if (mBuilder == null) {
+ if (nativeStaticLayoutPtr == 0) {
float[] w = mWidths;
w[mPos] = wid;
for (int i = mPos + 1, e = mPos + len; i < e; i++)
w[i] = 0;
} else {
- mBuilder.addReplacementRun(paint, mPos, mPos + len, wid);
+ StaticLayout.addReplacementRun(nativeStaticLayoutPtr, paint, mPos, mPos + len, wid);
}
mPos += len;
}
@@ -253,6 +253,11 @@ class MeasuredText {
return wid;
}
+ float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
+ Paint.FontMetricsInt fm) {
+ return addStyleRun(paint, spans, len, fm, 0 /* native ptr */);
+ }
+
int breakText(int limit, boolean forwards, float width) {
float[] w = mWidths;
if (forwards) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5c60188db1e4..c0fc44fd8ee1 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -32,6 +32,9 @@ import android.util.Pools.SynchronizedPool;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
import java.util.Arrays;
/**
@@ -57,9 +60,7 @@ public class StaticLayout extends Layout {
* default values.
*/
public final static class Builder {
- private Builder() {
- mNativePtr = nNewBuilder();
- }
+ private Builder() {}
/**
* Obtain a builder for constructing StaticLayout objects.
@@ -116,13 +117,11 @@ public class StaticLayout extends Layout {
b.mRightIndents = null;
b.mLeftPaddings = null;
b.mRightPaddings = null;
- nFinishBuilder(b.mNativePtr);
sPool.release(b);
}
// release any expensive state
/* package */ void finish() {
- nFinishBuilder(mNativePtr);
mText = null;
mPaint = null;
mLeftIndents = null;
@@ -405,32 +404,6 @@ public class StaticLayout extends Layout {
}
/**
- * Measurement and break iteration is done in native code. The protocol for using
- * the native code is as follows.
- *
- * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
- * stops, break strategy, and hyphenation frequency (and possibly other parameters in the
- * future).
- *
- * Then, for each run within the paragraph:
- * - one of the following, depending on the type of run:
- * + addStyleRun (a text run, to be measured in native code)
- * + addReplacementRun (a replacement run, width is given)
- *
- * Run nComputeLineBreaks() to obtain line breaks for the paragraph.
- *
- * After all paragraphs, call finish() to release expensive buffers.
- */
-
- /* package */ void addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
- nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
- }
-
- /* package */ void addReplacementRun(TextPaint paint, int start, int end, float width) {
- nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
- }
-
- /**
* Build the {@link StaticLayout} after options have been set.
*
* <p>Note: the builder object must not be reused in any way after calling this
@@ -446,17 +419,6 @@ public class StaticLayout extends Layout {
return result;
}
- @Override
- protected void finalize() throws Throwable {
- try {
- nFreeBuilder(mNativePtr);
- } finally {
- super.finalize();
- }
- }
-
- /* package */ long mNativePtr;
-
private CharSequence mText;
private int mStart;
private int mEnd;
@@ -694,270 +656,294 @@ public class StaticLayout extends Layout {
indents = null;
}
- int paraEnd;
- for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
- paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
- if (paraEnd < 0)
- paraEnd = bufEnd;
- else
- paraEnd++;
-
- int firstWidthLineCount = 1;
- int firstWidth = outerWidth;
- int restWidth = outerWidth;
-
- LineHeightSpan[] chooseHt = null;
-
- if (spanned != null) {
- LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
- LeadingMarginSpan.class);
- for (int i = 0; i < sp.length; i++) {
- LeadingMarginSpan lms = sp[i];
- firstWidth -= sp[i].getLeadingMargin(true);
- restWidth -= sp[i].getLeadingMargin(false);
-
- // LeadingMarginSpan2 is odd. The count affects all
- // leading margin spans, not just this particular one
- if (lms instanceof LeadingMarginSpan2) {
- LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
- firstWidthLineCount = Math.max(firstWidthLineCount,
- lms2.getLeadingMarginLineCount());
- }
+ final long nativePtr = nInit(
+ b.mBreakStrategy, b.mHyphenationFrequency,
+ // TODO: Support more justification mode, e.g. letter spacing, stretching.
+ b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
+ indents, mLeftPaddings, mRightPaddings);
+
+ try {
+ int paraEnd;
+ for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
+ paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
+ if (paraEnd < 0) {
+ paraEnd = bufEnd;
+ } else {
+ paraEnd++;
}
- chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
-
- if (chooseHt.length == 0) {
- chooseHt = null; // So that out() would not assume it has any contents
- } else {
- if (chooseHtv == null ||
- chooseHtv.length < chooseHt.length) {
- chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
+ int firstWidthLineCount = 1;
+ int firstWidth = outerWidth;
+ int restWidth = outerWidth;
+
+ LineHeightSpan[] chooseHt = null;
+
+ if (spanned != null) {
+ LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
+ LeadingMarginSpan.class);
+ for (int i = 0; i < sp.length; i++) {
+ LeadingMarginSpan lms = sp[i];
+ firstWidth -= sp[i].getLeadingMargin(true);
+ restWidth -= sp[i].getLeadingMargin(false);
+
+ // LeadingMarginSpan2 is odd. The count affects all
+ // leading margin spans, not just this particular one
+ if (lms instanceof LeadingMarginSpan2) {
+ LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
+ firstWidthLineCount = Math.max(firstWidthLineCount,
+ lms2.getLeadingMarginLineCount());
+ }
}
- for (int i = 0; i < chooseHt.length; i++) {
- int o = spanned.getSpanStart(chooseHt[i]);
+ chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
- if (o < paraStart) {
- // starts in this layout, before the
- // current paragraph
+ if (chooseHt.length == 0) {
+ chooseHt = null; // So that out() would not assume it has any contents
+ } else {
+ if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
+ chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
+ }
- chooseHtv[i] = getLineTop(getLineForOffset(o));
- } else {
- // starts in this paragraph
+ for (int i = 0; i < chooseHt.length; i++) {
+ int o = spanned.getSpanStart(chooseHt[i]);
+
+ if (o < paraStart) {
+ // starts in this layout, before the
+ // current paragraph
- chooseHtv[i] = v;
+ chooseHtv[i] = getLineTop(getLineForOffset(o));
+ } else {
+ // starts in this paragraph
+
+ chooseHtv[i] = v;
+ }
}
}
}
- }
- measured.setPara(source, paraStart, paraEnd, textDir, b);
- char[] chs = measured.mChars;
- float[] widths = measured.mWidths;
- byte[] chdirs = measured.mLevels;
- int dir = measured.mDir;
- boolean easy = measured.mEasy;
-
- // tab stop locations
- int[] variableTabStops = null;
- if (spanned != null) {
- TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
- paraEnd, TabStopSpan.class);
- if (spans.length > 0) {
- int[] stops = new int[spans.length];
- for (int i = 0; i < spans.length; i++) {
- stops[i] = spans[i].getTabStop();
+ measured.setPara(source, paraStart, paraEnd, textDir);
+ char[] chs = measured.mChars;
+ float[] widths = measured.mWidths;
+ byte[] chdirs = measured.mLevels;
+ int dir = measured.mDir;
+ boolean easy = measured.mEasy;
+
+ // tab stop locations
+ int[] variableTabStops = null;
+ if (spanned != null) {
+ TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+ paraEnd, TabStopSpan.class);
+ if (spans.length > 0) {
+ int[] stops = new int[spans.length];
+ for (int i = 0; i < spans.length; i++) {
+ stops[i] = spans[i].getTabStop();
+ }
+ Arrays.sort(stops, 0, stops.length);
+ variableTabStops = stops;
}
- Arrays.sort(stops, 0, stops.length);
- variableTabStops = stops;
- }
- }
-
- nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
- firstWidth, firstWidthLineCount, restWidth,
- variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency,
- // TODO: Support more justification mode, e.g. letter spacing, stretching.
- b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
- // TODO: indents and paddings don't need to get passed to native code for every
- // paragraph. Pass them to native code just once.
- indents, mLeftPaddings, mRightPaddings, mLineCount);
-
- // measurement has to be done before performing line breaking
- // but we don't want to recompute fontmetrics or span ranges the
- // second time, so we cache those and then use those stored values
- int fmCacheCount = 0;
- int spanEndCacheCount = 0;
- for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
- if (fmCacheCount * 4 >= fmCache.length) {
- int[] grow = new int[fmCacheCount * 4 * 2];
- System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
- fmCache = grow;
- }
-
- if (spanEndCacheCount >= spanEndCache.length) {
- int[] grow = new int[spanEndCacheCount * 2];
- System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
- spanEndCache = grow;
- }
-
- if (spanned == null) {
- spanEnd = paraEnd;
- int spanLen = spanEnd - spanStart;
- measured.addStyleRun(paint, spanLen, fm);
- } else {
- spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
- MetricAffectingSpan.class);
- int spanLen = spanEnd - spanStart;
- MetricAffectingSpan[] spans =
- spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
- spans = TextUtils.removeEmptySpans(spans, spanned, MetricAffectingSpan.class);
- measured.addStyleRun(paint, spans, spanLen, fm);
}
- // the order of storage here (top, bottom, ascent, descent) has to match the code below
- // where these values are retrieved
- fmCache[fmCacheCount * 4 + 0] = fm.top;
- fmCache[fmCacheCount * 4 + 1] = fm.bottom;
- fmCache[fmCacheCount * 4 + 2] = fm.ascent;
- fmCache[fmCacheCount * 4 + 3] = fm.descent;
- fmCacheCount++;
+ // measurement has to be done before performing line breaking
+ // but we don't want to recompute fontmetrics or span ranges the
+ // second time, so we cache those and then use those stored values
+ int fmCacheCount = 0;
+ int spanEndCacheCount = 0;
+ for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
+ if (fmCacheCount * 4 >= fmCache.length) {
+ int[] grow = new int[fmCacheCount * 4 * 2];
+ System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
+ fmCache = grow;
+ }
- spanEndCache[spanEndCacheCount] = spanEnd;
- spanEndCacheCount++;
- }
+ if (spanEndCacheCount >= spanEndCache.length) {
+ int[] grow = new int[spanEndCacheCount * 2];
+ System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
+ spanEndCache = grow;
+ }
- int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
- lineBreaks.widths, lineBreaks.ascents, lineBreaks.descents, lineBreaks.flags,
- lineBreaks.breaks.length, widths);
-
- final int[] breaks = lineBreaks.breaks;
- final float[] lineWidths = lineBreaks.widths;
- final float[] ascents = lineBreaks.ascents;
- final float[] descents = lineBreaks.descents;
- final int[] flags = lineBreaks.flags;
-
- final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
- final boolean ellipsisMayBeApplied = ellipsize != null
- && (ellipsize == TextUtils.TruncateAt.END
- || (mMaximumVisibleLineCount == 1
- && ellipsize != TextUtils.TruncateAt.MARQUEE));
- if (0 < remainingLineCount && remainingLineCount < breakCount
- && ellipsisMayBeApplied) {
- // Calculate width and flag.
- float width = 0;
- int flag = 0; // XXX May need to also have starting hyphen edit
- for (int i = remainingLineCount - 1; i < breakCount; i++) {
- if (i == breakCount - 1) {
- width += lineWidths[i];
+ if (spanned == null) {
+ spanEnd = paraEnd;
+ int spanLen = spanEnd - spanStart;
+ measured.addStyleRun(paint, spanLen, fm, nativePtr);
} else {
- for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
- width += widths[j];
- }
+ spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
+ MetricAffectingSpan.class);
+ int spanLen = spanEnd - spanStart;
+ MetricAffectingSpan[] spans =
+ spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
+ spans = TextUtils.removeEmptySpans(spans, spanned,
+ MetricAffectingSpan.class);
+ measured.addStyleRun(paint, spans, spanLen, fm, nativePtr);
}
- flag |= flags[i] & TAB_MASK;
- }
- // Treat the last line and overflowed lines as a single line.
- breaks[remainingLineCount - 1] = breaks[breakCount - 1];
- lineWidths[remainingLineCount - 1] = width;
- flags[remainingLineCount - 1] = flag;
- breakCount = remainingLineCount;
- }
+ // the order of storage here (top, bottom, ascent, descent) has to match the
+ // code below where these values are retrieved
+ fmCache[fmCacheCount * 4 + 0] = fm.top;
+ fmCache[fmCacheCount * 4 + 1] = fm.bottom;
+ fmCache[fmCacheCount * 4 + 2] = fm.ascent;
+ fmCache[fmCacheCount * 4 + 3] = fm.descent;
+ fmCacheCount++;
- // here is the offset of the starting character of the line we are currently measuring
- int here = paraStart;
-
- int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
- int fmCacheIndex = 0;
- int spanEndCacheIndex = 0;
- int breakIndex = 0;
- for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
- // retrieve end of span
- spanEnd = spanEndCache[spanEndCacheIndex++];
-
- // retrieve cached metrics, order matches above
- fm.top = fmCache[fmCacheIndex * 4 + 0];
- fm.bottom = fmCache[fmCacheIndex * 4 + 1];
- fm.ascent = fmCache[fmCacheIndex * 4 + 2];
- fm.descent = fmCache[fmCacheIndex * 4 + 3];
- fmCacheIndex++;
-
- if (fm.top < fmTop) {
- fmTop = fm.top;
- }
- if (fm.ascent < fmAscent) {
- fmAscent = fm.ascent;
- }
- if (fm.descent > fmDescent) {
- fmDescent = fm.descent;
- }
- if (fm.bottom > fmBottom) {
- fmBottom = fm.bottom;
+ spanEndCache[spanEndCacheCount] = spanEnd;
+ spanEndCacheCount++;
}
- // skip breaks ending before current span range
- while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
- breakIndex++;
+ int breakCount = nComputeLineBreaks(
+ nativePtr,
+
+ // Inputs
+ chs,
+ paraEnd - paraStart,
+ firstWidth,
+ firstWidthLineCount,
+ restWidth,
+ variableTabStops,
+ TAB_INCREMENT,
+ mLineCount,
+
+ // Outputs
+ lineBreaks,
+ lineBreaks.breaks.length,
+ lineBreaks.breaks,
+ lineBreaks.widths,
+ lineBreaks.ascents,
+ lineBreaks.descents,
+ lineBreaks.flags,
+ widths);
+
+ final int[] breaks = lineBreaks.breaks;
+ final float[] lineWidths = lineBreaks.widths;
+ final float[] ascents = lineBreaks.ascents;
+ final float[] descents = lineBreaks.descents;
+ final int[] flags = lineBreaks.flags;
+
+ final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
+ final boolean ellipsisMayBeApplied = ellipsize != null
+ && (ellipsize == TextUtils.TruncateAt.END
+ || (mMaximumVisibleLineCount == 1
+ && ellipsize != TextUtils.TruncateAt.MARQUEE));
+ if (0 < remainingLineCount && remainingLineCount < breakCount
+ && ellipsisMayBeApplied) {
+ // Calculate width and flag.
+ float width = 0;
+ int flag = 0; // XXX May need to also have starting hyphen edit
+ for (int i = remainingLineCount - 1; i < breakCount; i++) {
+ if (i == breakCount - 1) {
+ width += lineWidths[i];
+ } else {
+ for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
+ width += widths[j];
+ }
+ }
+ flag |= flags[i] & TAB_MASK;
+ }
+ // Treat the last line and overflowed lines as a single line.
+ breaks[remainingLineCount - 1] = breaks[breakCount - 1];
+ lineWidths[remainingLineCount - 1] = width;
+ flags[remainingLineCount - 1] = flag;
+
+ breakCount = remainingLineCount;
}
- while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
- int endPos = paraStart + breaks[breakIndex];
-
- boolean moreChars = (endPos < bufEnd);
-
- final int ascent = fallbackLineSpacing
- ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
- : fmAscent;
- final int descent = fallbackLineSpacing
- ? Math.max(fmDescent, Math.round(descents[breakIndex]))
- : fmDescent;
- v = out(source, here, endPos,
- ascent, descent, fmTop, fmBottom,
- v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex],
- needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
- addLastLineSpacing, chs, widths, paraStart, ellipsize,
- ellipsizedWidth, lineWidths[breakIndex], paint, moreChars);
-
- if (endPos < spanEnd) {
- // preserve metrics for current span
+ // here is the offset of the starting character of the line we are currently
+ // measuring
+ int here = paraStart;
+
+ int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
+ int fmCacheIndex = 0;
+ int spanEndCacheIndex = 0;
+ int breakIndex = 0;
+ for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
+ // retrieve end of span
+ spanEnd = spanEndCache[spanEndCacheIndex++];
+
+ // retrieve cached metrics, order matches above
+ fm.top = fmCache[fmCacheIndex * 4 + 0];
+ fm.bottom = fmCache[fmCacheIndex * 4 + 1];
+ fm.ascent = fmCache[fmCacheIndex * 4 + 2];
+ fm.descent = fmCache[fmCacheIndex * 4 + 3];
+ fmCacheIndex++;
+
+ if (fm.top < fmTop) {
fmTop = fm.top;
- fmBottom = fm.bottom;
+ }
+ if (fm.ascent < fmAscent) {
fmAscent = fm.ascent;
+ }
+ if (fm.descent > fmDescent) {
fmDescent = fm.descent;
- } else {
- fmTop = fmBottom = fmAscent = fmDescent = 0;
}
+ if (fm.bottom > fmBottom) {
+ fmBottom = fm.bottom;
+ }
+
+ // skip breaks ending before current span range
+ while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
+ breakIndex++;
+ }
+
+ while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
+ int endPos = paraStart + breaks[breakIndex];
+
+ boolean moreChars = (endPos < bufEnd);
+
+ final int ascent = fallbackLineSpacing
+ ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
+ : fmAscent;
+ final int descent = fallbackLineSpacing
+ ? Math.max(fmDescent, Math.round(descents[breakIndex]))
+ : fmDescent;
+ v = out(source, here, endPos,
+ ascent, descent, fmTop, fmBottom,
+ v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
+ flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd,
+ includepad, trackpad, addLastLineSpacing, chs, widths, paraStart,
+ ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint,
+ moreChars);
+
+ if (endPos < spanEnd) {
+ // preserve metrics for current span
+ fmTop = fm.top;
+ fmBottom = fm.bottom;
+ fmAscent = fm.ascent;
+ fmDescent = fm.descent;
+ } else {
+ fmTop = fmBottom = fmAscent = fmDescent = 0;
+ }
- here = endPos;
- breakIndex++;
+ here = endPos;
+ breakIndex++;
- if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
- return;
+ if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
+ return;
+ }
}
}
- }
- if (paraEnd == bufEnd)
- break;
- }
+ if (paraEnd == bufEnd) {
+ break;
+ }
+ }
- if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) &&
- mLineCount < mMaximumVisibleLineCount) {
- measured.setPara(source, bufEnd, bufEnd, textDir, b);
-
- paint.getFontMetricsInt(fm);
-
- v = out(source,
- bufEnd, bufEnd, fm.ascent, fm.descent,
- fm.top, fm.bottom,
- v,
- spacingmult, spacingadd, null,
- null, fm, 0,
- needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
- includepad, trackpad, addLastLineSpacing, null,
- null, bufStart, ellipsize,
- ellipsizedWidth, 0, paint, false);
+ if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
+ && mLineCount < mMaximumVisibleLineCount) {
+ measured.setPara(source, bufEnd, bufEnd, textDir);
+
+ paint.getFontMetricsInt(fm);
+
+ v = out(source,
+ bufEnd, bufEnd, fm.ascent, fm.descent,
+ fm.top, fm.bottom,
+ v,
+ spacingmult, spacingadd, null,
+ null, fm, 0,
+ needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
+ includepad, trackpad, addLastLineSpacing, null,
+ null, bufStart, ellipsize,
+ ellipsizedWidth, 0, paint, false);
+ }
+ } finally {
+ nFinish(nativePtr);
}
}
@@ -1487,26 +1473,51 @@ public class StaticLayout extends Layout {
mMaxLineHeight : super.getHeight();
}
- private static native long nNewBuilder();
- private static native void nFreeBuilder(long nativePtr);
- private static native void nFinishBuilder(long nativePtr);
-
- // Set up paragraph text and settings; done as one big method to minimize jni crossings
- private static native void nSetupParagraph(
- /* non zero */ long nativePtr, @NonNull char[] text, @IntRange(from = 0) int length,
- @FloatRange(from = 0.0f) float firstWidth, @IntRange(from = 0) int firstWidthLineCount,
- @FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops,
- int defaultTabStop, @BreakStrategy int breakStrategy,
- @HyphenationFrequency int hyphenationFrequency, boolean isJustified,
- @Nullable int[] indents, @Nullable int[] leftPaddings, @Nullable int[] rightPaddings,
- @IntRange(from = 0) int indentsOffset);
-
- // TODO: Make this method CriticalNative once native code defers doing layouts.
+ /**
+ * Measurement and break iteration is done in native code. The protocol for using
+ * the native code is as follows.
+ *
+ * First, call nInit to setup native line breaker object. Then, for each paragraph, do the
+ * following:
+ *
+ * - Call one of the following methods for each run within the paragraph depending on the type
+ * of run:
+ * + addStyleRun (a text run, to be measured in native code)
+ * + addReplacementRun (a replacement run, width is given)
+ *
+ * - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
+ *
+ * After all paragraphs, call finish() to release expensive buffers.
+ */
+
+ /* package */ static void addStyleRun(long nativePtr, TextPaint paint, int start, int end,
+ boolean isRtl) {
+ nAddStyleRun(nativePtr, paint.getNativeInstance(), start, end, isRtl);
+ }
+
+ /* package */ static void addReplacementRun(long nativePtr, TextPaint paint, int start, int end,
+ float width) {
+ nAddReplacementRun(nativePtr, paint.getNativeInstance(), start, end, width);
+ }
+
+ @FastNative
+ private static native long nInit(
+ @BreakStrategy int breakStrategy,
+ @HyphenationFrequency int hyphenationFrequency,
+ boolean isJustified,
+ @Nullable int[] indents,
+ @Nullable int[] leftPaddings,
+ @Nullable int[] rightPaddings);
+
+ @CriticalNative
+ private static native void nFinish(long nativePtr);
+
+ @CriticalNative
private static native void nAddStyleRun(
/* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
@IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl);
- // TODO: Make this method CriticalNative once native code defers doing layouts.
+ @CriticalNative
private static native void nAddReplacementRun(
/* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@@ -1519,10 +1530,28 @@ public class StaticLayout extends Layout {
// arrays do not have to be resized
// The individual character widths will be returned in charWidths. The length of charWidths must
// be at least the length of the text.
- private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
- int[] recycleBreaks, float[] recycleWidths, float[] recycleAscents,
- float[] recycleDescents, int[] recycleFlags, int recycleLength,
- float[] charWidths);
+ private static native int nComputeLineBreaks(
+ /* non zero */ long nativePtr,
+
+ // Inputs
+ @NonNull char[] text,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ @Nullable int[] variableTabStops,
+ int defaultTabStop,
+ @IntRange(from = 0) int indentsOffset,
+
+ // Outputs
+ @NonNull LineBreaks recycle,
+ @IntRange(from = 0) int recycleLength,
+ @NonNull int[] recycleBreaks,
+ @NonNull float[] recycleWidths,
+ @NonNull float[] recycleAscents,
+ @NonNull float[] recycleDescents,
+ @NonNull int[] recycleFlags,
+ @NonNull float[] charWidths);
private int mLineCount;
private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 20c0ed87285a..86cc0141b0a4 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -28,6 +28,7 @@ import android.text.style.MetricAffectingSpan;
import android.text.style.ReplacementSpan;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
@@ -44,7 +45,8 @@ import java.util.ArrayList;
*
* @hide
*/
-class TextLine {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class TextLine {
private static final boolean DEBUG = false;
private TextPaint mPaint;
@@ -82,7 +84,8 @@ class TextLine {
*
* @return an uninitialized TextLine
*/
- static TextLine obtain() {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public static TextLine obtain() {
TextLine tl;
synchronized (sCached) {
for (int i = sCached.length; --i >= 0;) {
@@ -107,7 +110,8 @@ class TextLine {
* @return null, as a convenience from clearing references to the provided
* TextLine
*/
- static TextLine recycle(TextLine tl) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public static TextLine recycle(TextLine tl) {
tl.mText = null;
tl.mPaint = null;
tl.mDirections = null;
@@ -142,7 +146,8 @@ class TextLine {
* @param hasTabs true if the line might contain tabs
* @param tabStops the tabStops. Can be null.
*/
- void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
Directions directions, boolean hasTabs, TabStops tabStops) {
mPaint = paint;
mText = text;
@@ -196,7 +201,8 @@ class TextLine {
/**
* Justify the line to the given width.
*/
- void justify(float justifyWidth) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void justify(float justifyWidth) {
int end = mLen;
while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) {
end--;
@@ -277,7 +283,8 @@ class TextLine {
* @param fmi receives font metrics information, can be null
* @return the signed width of the line
*/
- float metrics(FontMetricsInt fmi) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public float metrics(FontMetricsInt fmi) {
return measure(mLen, false, fmi);
}
@@ -1165,23 +1172,18 @@ class TextLine {
}
private boolean isStretchableWhitespace(int ch) {
- // TODO: Support other stretchable whitespace. (Bug: 34013491)
- return ch == 0x0020 || ch == 0x00A0;
- }
-
- private int nextStretchableSpace(int start, int end) {
- for (int i = start; i < end; i++) {
- final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart);
- if (isStretchableWhitespace(c)) return i;
- }
- return end;
+ // TODO: Support NBSP and other stretchable whitespace (b/34013491 and b/68204709).
+ return ch == 0x0020;
}
/* Return the number of spaces in the text line, for the purpose of justification */
private int countStretchableSpaces(int start, int end) {
int count = 0;
- for (int i = start; i < end; i = nextStretchableSpace(i + 1, end)) {
- count++;
+ for (int i = start; i < end; i++) {
+ final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart);
+ if (isStretchableWhitespace(c)) {
+ count++;
+ }
}
return count;
}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 68afeecfc996..cbdaa69b5aa0 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1519,7 +1519,7 @@ public class TextUtils {
}
// XXX this is probably ok, but need to look at it more
- tempMt.setPara(format, 0, format.length(), textDir, null);
+ tempMt.setPara(format, 0, format.length(), textDir);
float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null);
if (w + moreWid <= avail) {
@@ -1541,7 +1541,7 @@ public class TextUtils {
private static float setPara(MeasuredText mt, TextPaint paint,
CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
- mt.setPara(text, start, end, textDir, null);
+ mt.setPara(text, start, end, textDir);
float width;
Spanned sp = text instanceof Spanned ? (Spanned) text : null;
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index cf36f4360c3b..dc50fa1d6036 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -44,7 +44,7 @@ public class TouchDelegate {
/**
* mBounds inflated to include some slop. This rect is to track whether the motion events
- * should be considered to be be within the delegate view.
+ * should be considered to be within the delegate view.
*/
private Rect mSlopBounds;
@@ -64,14 +64,12 @@ public class TouchDelegate {
public static final int BELOW = 2;
/**
- * The touchable region of the View extends to the left of its
- * actual extent.
+ * The touchable region of the View extends to the left of its actual extent.
*/
public static final int TO_LEFT = 4;
/**
- * The touchable region of the View extends to the right of its
- * actual extent.
+ * The touchable region of the View extends to the right of its actual extent.
*/
public static final int TO_RIGHT = 8;
@@ -108,28 +106,24 @@ public class TouchDelegate {
boolean handled = false;
switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- Rect bounds = mBounds;
-
- if (bounds.contains(x, y)) {
- mDelegateTargeted = true;
- sendToDelegate = true;
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_MOVE:
- sendToDelegate = mDelegateTargeted;
- if (sendToDelegate) {
- Rect slopBounds = mSlopBounds;
- if (!slopBounds.contains(x, y)) {
- hit = false;
+ case MotionEvent.ACTION_DOWN:
+ mDelegateTargeted = mBounds.contains(x, y);
+ sendToDelegate = mDelegateTargeted;
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
+ sendToDelegate = mDelegateTargeted;
+ if (sendToDelegate) {
+ Rect slopBounds = mSlopBounds;
+ if (!slopBounds.contains(x, y)) {
+ hit = false;
+ }
}
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- sendToDelegate = mDelegateTargeted;
- mDelegateTargeted = false;
- break;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ sendToDelegate = mDelegateTargeted;
+ mDelegateTargeted = false;
+ break;
}
if (sendToDelegate) {
final View delegateView = mDelegateView;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 8c3b8a2e6b20..26d2141e3486 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -33,6 +33,52 @@ import java.util.List;
/**
* Information for generating a widget to handle classified text.
+ *
+ * <p>A TextClassification object contains icons, labels, onClickListeners and intents that may
+ * be used to build a widget that can be used to act on classified text.
+ *
+ * <p>e.g. building a view that, when clicked, shares the classified text with the preferred app:
+ *
+ * <pre>{@code
+ * // Called preferably outside the UiThread.
+ * TextClassification classification = textClassifier.classifyText(allText, 10, 25, null);
+ *
+ * // Called on the UiThread.
+ * Button button = new Button(context);
+ * button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
+ * button.setText(classification.getLabel());
+ * button.setOnClickListener(classification.getOnClickListener());
+ * }</pre>
+ *
+ * <p>e.g. starting an action mode with menu items that can handle the classified text:
+ *
+ * <pre>{@code
+ * // Called preferably outside the UiThread.
+ * final TextClassification classification = textClassifier.classifyText(allText, 10, 25, null);
+ *
+ * // Called on the UiThread.
+ * view.startActionMode(new ActionMode.Callback() {
+ *
+ * public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ * for (int i = 0; i < classification.getActionCount(); i++) {
+ * if (thisAppHasPermissionToInvokeIntent(classification.getIntent(i))) {
+ * menu.add(Menu.NONE, i, 20, classification.getLabel(i))
+ * .setIcon(classification.getIcon(i))
+ * .setIntent(classification.getIntent(i));
+ * }
+ * }
+ * return true;
+ * }
+ *
+ * public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ * context.startActivity(item.getIntent());
+ * return true;
+ * }
+ *
+ * ...
+ * });
+ * }</pre>
+ *
*/
public final class TextClassification {
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index c3601d9d32be..46dbd0e34a1c 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -29,8 +29,8 @@ import java.lang.annotation.RetentionPolicy;
/**
* Interface for providing text classification related features.
*
- * <p>Unless otherwise stated, methods of this interface are blocking operations and you should
- * avoid calling them on the UI thread.
+ * <p>Unless otherwise stated, methods of this interface are blocking operations.
+ * Avoid calling them on the UI thread.
*/
public interface TextClassifier {
@@ -75,8 +75,8 @@ public interface TextClassifier {
};
/**
- * Returns suggested text selection indices, recognized types and their associated confidence
- * scores. The selections are ordered from highest to lowest scoring.
+ * Returns suggested text selection start and end indices, recognized entity types, and their
+ * associated confidence scores. The entity types are ordered from highest to lowest scoring.
*
* @param text text providing context for the selected text (which is specified
* by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 95cb4549cec1..48e427fd53b5 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -25,7 +25,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
-import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
@@ -445,38 +444,17 @@ public final class WebViewFactory {
}
}
- private static int prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
- if (DEBUG) Log.v(LOGTAG, "creating relro files");
- int numRelros = 0;
-
- // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any
- // unexpected values will be handled there to ensure that we trigger notifying any process
- // waiting on relro creation.
- if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
- if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
- WebViewLibraryLoader.createRelroFile(false /* is64Bit */, nativeLibraryPaths[0]);
- numRelros++;
- }
-
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
- WebViewLibraryLoader.createRelroFile(true /* is64Bit */, nativeLibraryPaths[1]);
- numRelros++;
- }
- return numRelros;
- }
-
/**
* @hide
*/
public static int onWebViewProviderChanged(PackageInfo packageInfo) {
- String[] nativeLibs = null;
+ int startedRelroProcesses = 0;
ApplicationInfo originalAppInfo = new ApplicationInfo(packageInfo.applicationInfo);
try {
fixupStubApplicationInfo(packageInfo.applicationInfo,
AppGlobals.getInitialApplication().getPackageManager());
- nativeLibs = WebViewLibraryLoader.updateWebViewZygoteVmSize(packageInfo);
+ startedRelroProcesses = WebViewLibraryLoader.prepareNativeLibraries(packageInfo);
} catch (Throwable t) {
// Log and discard errors at this stage as we must not crash the system server.
Log.e(LOGTAG, "error preparing webview native library", t);
@@ -484,7 +462,7 @@ public final class WebViewFactory {
WebViewZygote.onWebViewProviderChanged(packageInfo, originalAppInfo);
- return prepareWebViewInSystemServer(nativeLibs);
+ return startedRelroProcesses;
}
private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index fa1a3907b1f9..341c69fd2eba 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -26,6 +28,7 @@ import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import dalvik.system.VMRuntime;
@@ -36,7 +39,11 @@ import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-class WebViewLibraryLoader {
+/**
+ * @hide
+ */
+@VisibleForTesting
+public class WebViewLibraryLoader {
private static final String LOGTAG = WebViewLibraryLoader.class.getSimpleName();
private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
@@ -94,7 +101,7 @@ class WebViewLibraryLoader {
/**
* Create a single relro file by invoking an isolated process that to do the actual work.
*/
- static void createRelroFile(final boolean is64Bit, String nativeLibraryPath) {
+ static void createRelroFile(final boolean is64Bit, @NonNull WebViewNativeLibrary nativeLib) {
final String abi =
is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
@@ -112,12 +119,12 @@ class WebViewLibraryLoader {
};
try {
- if (nativeLibraryPath == null) {
+ if (nativeLib == null || nativeLib.path == null) {
throw new IllegalArgumentException(
"Native library paths to the WebView RelRo process must not be null!");
}
int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
- RelroFileCreator.class.getName(), new String[] { nativeLibraryPath },
+ RelroFileCreator.class.getName(), new String[] { nativeLib.path },
"WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
} catch (Throwable t) {
@@ -128,56 +135,77 @@ class WebViewLibraryLoader {
}
/**
+ * Perform preparations needed to allow loading WebView from an application. This method should
+ * be called whenever we change WebView provider.
+ * @return the number of relro processes started.
+ */
+ static int prepareNativeLibraries(PackageInfo webviewPackageInfo)
+ throws WebViewFactory.MissingWebViewPackageException {
+ WebViewNativeLibrary nativeLib32bit =
+ getWebViewNativeLibrary(webviewPackageInfo, false /* is64bit */);
+ WebViewNativeLibrary nativeLib64bit =
+ getWebViewNativeLibrary(webviewPackageInfo, true /* is64bit */);
+ updateWebViewZygoteVmSize(nativeLib32bit, nativeLib64bit);
+
+ return createRelros(nativeLib32bit, nativeLib64bit);
+ }
+
+ /**
+ * @return the number of relro processes started.
+ */
+ private static int createRelros(@Nullable WebViewNativeLibrary nativeLib32bit,
+ @Nullable WebViewNativeLibrary nativeLib64bit) {
+ if (DEBUG) Log.v(LOGTAG, "creating relro files");
+ int numRelros = 0;
+
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ if (nativeLib32bit == null) {
+ Log.e(LOGTAG, "No 32-bit WebView library path, skipping relro creation.");
+ } else {
+ if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
+ createRelroFile(false /* is64Bit */, nativeLib32bit);
+ numRelros++;
+ }
+ }
+
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ if (nativeLib64bit == null) {
+ Log.e(LOGTAG, "No 64-bit WebView library path, skipping relro creation.");
+ } else {
+ if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
+ createRelroFile(true /* is64Bit */, nativeLib64bit);
+ numRelros++;
+ }
+ }
+ return numRelros;
+ }
+
+ /**
*
* @return the native WebView libraries in the new WebView APK.
*/
- static String[] updateWebViewZygoteVmSize(PackageInfo packageInfo)
+ private static void updateWebViewZygoteVmSize(
+ @Nullable WebViewNativeLibrary nativeLib32bit,
+ @Nullable WebViewNativeLibrary nativeLib64bit)
throws WebViewFactory.MissingWebViewPackageException {
// Find the native libraries of the new WebView package, to change the size of the
// memory region in the Zygote reserved for the library.
- String[] nativeLibs = getWebViewNativeLibraryPaths(packageInfo);
- if (nativeLibs != null) {
- long newVmSize = 0L;
-
- for (String path : nativeLibs) {
- if (path == null || TextUtils.isEmpty(path)) continue;
- if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path);
- File f = new File(path);
- if (f.exists()) {
- newVmSize = Math.max(newVmSize, f.length());
- continue;
- }
- if (path.contains("!/")) {
- String[] split = TextUtils.split(path, "!/");
- if (split.length == 2) {
- try (ZipFile z = new ZipFile(split[0])) {
- ZipEntry e = z.getEntry(split[1]);
- if (e != null && e.getMethod() == ZipEntry.STORED) {
- newVmSize = Math.max(newVmSize, e.getSize());
- continue;
- }
- }
- catch (IOException e) {
- Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e);
- }
- }
- }
- Log.e(LOGTAG, "error sizing load for " + path);
- }
+ long newVmSize = 0L;
- if (DEBUG) {
- Log.v(LOGTAG, "Based on library size, need " + newVmSize
- + " bytes of address space.");
- }
- // The required memory can be larger than the file on disk (due to .bss), and an
- // upgraded version of the library will likely be larger, so always attempt to
- // reserve twice as much as we think to allow for the library to grow during this
- // boot cycle.
- newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
- Log.d(LOGTAG, "Setting new address space to " + newVmSize);
- setWebViewZygoteVmSize(newVmSize);
+ if (nativeLib32bit != null) newVmSize = Math.max(newVmSize, nativeLib32bit.size);
+ if (nativeLib64bit != null) newVmSize = Math.max(newVmSize, nativeLib64bit.size);
+
+ if (DEBUG) {
+ Log.v(LOGTAG, "Based on library size, need " + newVmSize
+ + " bytes of address space.");
}
- return nativeLibs;
+ // The required memory can be larger than the file on disk (due to .bss), and an
+ // upgraded version of the library will likely be larger, so always attempt to
+ // reserve twice as much as we think to allow for the library to grow during this
+ // boot cycle.
+ newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
+ Log.d(LOGTAG, "Setting new address space to " + newVmSize);
+ setWebViewZygoteVmSize(newVmSize);
}
/**
@@ -227,64 +255,78 @@ class WebViewLibraryLoader {
/**
* Fetch WebView's native library paths from {@param packageInfo}.
+ * @hide
*/
- static String[] getWebViewNativeLibraryPaths(PackageInfo packageInfo)
- throws WebViewFactory.MissingWebViewPackageException {
+ @Nullable
+ @VisibleForTesting
+ public static WebViewNativeLibrary getWebViewNativeLibrary(PackageInfo packageInfo,
+ boolean is64bit) throws WebViewFactory.MissingWebViewPackageException {
ApplicationInfo ai = packageInfo.applicationInfo;
final String nativeLibFileName = WebViewFactory.getWebViewLibrary(ai);
- String path32;
- String path64;
- boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);
- if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
- // Multi-arch case.
- if (primaryArchIs64bit) {
- // Primary arch: 64-bit, secondary: 32-bit.
- path64 = ai.nativeLibraryDir;
- path32 = ai.secondaryNativeLibraryDir;
- } else {
- // Primary arch: 32-bit, secondary: 64-bit.
- path64 = ai.secondaryNativeLibraryDir;
- path32 = ai.nativeLibraryDir;
- }
- } else if (primaryArchIs64bit) {
- // Single-arch 64-bit.
- path64 = ai.nativeLibraryDir;
- path32 = "";
- } else {
- // Single-arch 32-bit.
- path32 = ai.nativeLibraryDir;
- path64 = "";
+ String dir = getWebViewNativeLibraryDirectory(ai, is64bit /* 64bit */);
+
+ WebViewNativeLibrary lib = findNativeLibrary(ai, nativeLibFileName,
+ is64bit ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS, dir);
+
+ if (DEBUG) {
+ Log.v(LOGTAG, String.format("Native %d-bit lib: %s", is64bit ? 64 : 32, lib.path));
}
+ return lib;
+ }
- // Form the full paths to the extracted native libraries.
- // If libraries were not extracted, try load from APK paths instead.
- if (!TextUtils.isEmpty(path32)) {
- path32 += "/" + nativeLibFileName;
- File f = new File(path32);
- if (!f.exists()) {
- path32 = getLoadFromApkPath(ai.sourceDir,
- Build.SUPPORTED_32_BIT_ABIS,
- nativeLibFileName);
- }
+ /**
+ * @return the directory of the native WebView library with bitness {@param is64bit}.
+ * @hide
+ */
+ @VisibleForTesting
+ public static String getWebViewNativeLibraryDirectory(ApplicationInfo ai, boolean is64bit) {
+ // Primary arch has the same bitness as the library we are looking for.
+ if (is64bit == VMRuntime.is64BitAbi(ai.primaryCpuAbi)) return ai.nativeLibraryDir;
+
+ // Secondary arch has the same bitness as the library we are looking for.
+ if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
+ return ai.secondaryNativeLibraryDir;
}
- if (!TextUtils.isEmpty(path64)) {
- path64 += "/" + nativeLibFileName;
- File f = new File(path64);
- if (!f.exists()) {
- path64 = getLoadFromApkPath(ai.sourceDir,
- Build.SUPPORTED_64_BIT_ABIS,
- nativeLibFileName);
- }
+
+ return "";
+ }
+
+ /**
+ * @return an object describing a native WebView library given the directory path of that
+ * library, or null if the library couldn't be found.
+ */
+ @Nullable
+ private static WebViewNativeLibrary findNativeLibrary(ApplicationInfo ai,
+ String nativeLibFileName, String[] abiList, String libDirectory)
+ throws WebViewFactory.MissingWebViewPackageException {
+ if (TextUtils.isEmpty(libDirectory)) return null;
+ String libPath = libDirectory + "/" + nativeLibFileName;
+ File f = new File(libPath);
+ if (f.exists()) {
+ return new WebViewNativeLibrary(libPath, f.length());
+ } else {
+ return getLoadFromApkPath(ai.sourceDir, abiList, nativeLibFileName);
}
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static class WebViewNativeLibrary {
+ public final String path;
+ public final long size;
- if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64);
- return new String[] { path32, path64 };
+ WebViewNativeLibrary(String path, long size) {
+ this.path = path;
+ this.size = size;
+ }
}
- private static String getLoadFromApkPath(String apkPath,
- String[] abiList,
- String nativeLibFileName)
+ private static WebViewNativeLibrary getLoadFromApkPath(String apkPath,
+ String[] abiList,
+ String nativeLibFileName)
throws WebViewFactory.MissingWebViewPackageException {
// Search the APK for a native library conforming to a listed ABI.
try (ZipFile z = new ZipFile(apkPath)) {
@@ -293,13 +335,13 @@ class WebViewLibraryLoader {
ZipEntry e = z.getEntry(entry);
if (e != null && e.getMethod() == ZipEntry.STORED) {
// Return a path formatted for dlopen() load from APK.
- return apkPath + "!/" + entry;
+ return new WebViewNativeLibrary(apkPath + "!/" + entry, e.getSize());
}
}
} catch (IOException e) {
throw new WebViewFactory.MissingWebViewPackageException(e);
}
- return "";
+ return null;
}
/**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index af0959239b6f..e6da69dc95c7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -129,7 +129,7 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
-
+import java.util.Map;
/**
* Helper class used by TextView to handle editable text views.
@@ -163,9 +163,9 @@ public class Editor {
private static final int MENU_ITEM_ORDER_REPLACE = 9;
private static final int MENU_ITEM_ORDER_AUTOFILL = 10;
private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11;
+ private static final int MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START = 50;
private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
- private static final float MAGNIFIER_ZOOM = 1.25f;
@IntDef({MagnifierHandleTrigger.SELECTION_START,
MagnifierHandleTrigger.SELECTION_END,
MagnifierHandleTrigger.INSERTION})
@@ -476,17 +476,6 @@ public class Editor {
stopTextActionModeWithPreservingSelection();
}
- void invalidateMagnifier() {
- final DisplayMetrics dm = mTextView.getResources().getDisplayMetrics();
- invalidateMagnifier(0, 0, dm.widthPixels, dm.heightPixels);
- }
-
- void invalidateMagnifier(final float l, final float t, final float r, final float b) {
- if (mMagnifier != null) {
- mTextView.post(() -> mMagnifier.invalidate(new RectF(l, t, r, b)));
- }
- }
-
private void discardTextDisplayLists() {
if (mTextRenderNodes != null) {
for (int i = 0; i < mTextRenderNodes.length; i++) {
@@ -3804,6 +3793,7 @@ public class Editor {
private final RectF mSelectionBounds = new RectF();
private final boolean mHasSelection;
private final int mHandleHeight;
+ private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>();
public TextActionModeCallback(boolean hasSelection) {
mHasSelection = hasSelection;
@@ -3831,6 +3821,8 @@ public class Editor {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mAssistClickHandlers.clear();
+
mode.setTitle(null);
mode.setSubtitle(null);
mode.setTitleOptionalHint(true);
@@ -3914,14 +3906,14 @@ public class Editor {
updateSelectAllItem(menu);
updateReplaceItem(menu);
- updateAssistMenuItem(menu);
+ updateAssistMenuItems(menu);
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
updateSelectAllItem(menu);
updateReplaceItem(menu);
- updateAssistMenuItem(menu);
+ updateAssistMenuItems(menu);
Callback customCallback = getCustomCallback();
if (customCallback != null) {
@@ -3954,32 +3946,118 @@ public class Editor {
}
}
- private void updateAssistMenuItem(Menu menu) {
- menu.removeItem(TextView.ID_ASSIST);
+ private void updateAssistMenuItems(Menu menu) {
+ clearAssistMenuItems(menu);
+ if (!mTextView.isDeviceProvisioned()) {
+ return;
+ }
final TextClassification textClassification =
getSelectionActionModeHelper().getTextClassification();
- if (canAssist()) {
- menu.add(TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST,
- textClassification.getLabel())
- .setIcon(textClassification.getIcon())
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- mMetricsLogger.write(
- new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
- .setType(MetricsEvent.TYPE_OPEN)
- .setSubtype(textClassification.getLogType()));
+ final int count = textClassification != null ? textClassification.getActionCount() : 0;
+ for (int i = 0; i < count; i++) {
+ if (!isValidAssistMenuItem(i)) {
+ continue;
+ }
+ final int groupId = TextView.ID_ASSIST;
+ final int order = (i == 0)
+ ? MENU_ITEM_ORDER_ASSIST
+ : MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i;
+ final int id = (i == 0) ? TextView.ID_ASSIST : Menu.NONE;
+ final int showAsFlag = (i == 0)
+ ? MenuItem.SHOW_AS_ACTION_ALWAYS
+ : MenuItem.SHOW_AS_ACTION_NEVER;
+ final MenuItem item = menu.add(
+ groupId, id, order, textClassification.getLabel(i))
+ .setIcon(textClassification.getIcon(i))
+ .setIntent(textClassification.getIntent(i));
+ item.setShowAsAction(showAsFlag);
+ mAssistClickHandlers.put(item, textClassification.getOnClickListener(i));
+ if (id == TextView.ID_ASSIST) {
+ mMetricsLogger.write(
+ new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
+ .setType(MetricsEvent.TYPE_OPEN)
+ .setSubtype(textClassification.getLogType()));
+ }
+ }
+ }
+
+ private void clearAssistMenuItems(Menu menu) {
+ int i = 0;
+ while (i < menu.size()) {
+ final MenuItem menuItem = menu.getItem(i);
+ if (menuItem.getGroupId() == TextView.ID_ASSIST) {
+ menu.removeItem(menuItem.getItemId());
+ continue;
+ }
+ i++;
}
}
- private boolean canAssist() {
+ private boolean isValidAssistMenuItem(int index) {
final TextClassification textClassification =
getSelectionActionModeHelper().getTextClassification();
- return mTextView.isDeviceProvisioned()
- && textClassification != null
- && (textClassification.getIcon() != null
- || !TextUtils.isEmpty(textClassification.getLabel()))
- && (textClassification.getOnClickListener() != null
- || (textClassification.getIntent() != null
- && mTextView.getContext().canStartActivityForResult()));
+ if (!mTextView.isDeviceProvisioned() || textClassification == null
+ || index < 0 || index >= textClassification.getActionCount()) {
+ return false;
+ }
+ final Drawable icon = textClassification.getIcon(index);
+ final CharSequence label = textClassification.getLabel(index);
+ final boolean hasUi = icon != null || !TextUtils.isEmpty(label);
+ final OnClickListener onClick = textClassification.getOnClickListener(index);
+ final Intent intent = textClassification.getIntent(index);
+ final boolean hasAction = onClick != null || isSupportedIntent(intent);
+ return hasUi && hasAction;
+ }
+
+ private boolean isSupportedIntent(Intent intent) {
+ if (intent == null) {
+ return false;
+ }
+ final Context context = mTextView.getContext();
+ final ResolveInfo info = context.getPackageManager().resolveActivity(intent, 0);
+ final boolean samePackage = context.getPackageName().equals(
+ info.activityInfo.packageName);
+ if (samePackage) {
+ return true;
+ }
+
+ final boolean exported = info.activityInfo.exported;
+ final boolean requiresPermission = info.activityInfo.permission != null;
+ final boolean hasPermission = !requiresPermission
+ || context.checkSelfPermission(info.activityInfo.permission)
+ == PackageManager.PERMISSION_GRANTED;
+ return exported && hasPermission;
+ }
+
+ private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) {
+ Preconditions.checkArgument(assistMenuItem.getGroupId() == TextView.ID_ASSIST);
+
+ final TextClassification textClassification =
+ getSelectionActionModeHelper().getTextClassification();
+ if (!mTextView.isDeviceProvisioned() || textClassification == null) {
+ // No textClassification result to handle the click. Eat the click.
+ return true;
+ }
+
+ OnClickListener onClickListener = mAssistClickHandlers.get(assistMenuItem);
+ if (onClickListener == null) {
+ final Intent intent = assistMenuItem.getIntent();
+ if (intent != null) {
+ onClickListener = TextClassification.createStartActivityOnClickListener(
+ mTextView.getContext(), intent);
+ }
+ }
+ if (onClickListener != null) {
+ onClickListener.onClick(mTextView);
+ stopTextActionMode();
+ if (assistMenuItem.getItemId() == TextView.ID_ASSIST) {
+ mMetricsLogger.action(
+ MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST,
+ textClassification.getLogType());
+ }
+ }
+ // We tried our best.
+ return true;
}
@Override
@@ -3993,25 +4071,7 @@ public class Editor {
if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
return true;
}
- final TextClassification textClassification =
- getSelectionActionModeHelper().getTextClassification();
- if (TextView.ID_ASSIST == item.getItemId() && textClassification != null) {
- final OnClickListener onClickListener =
- textClassification.getOnClickListener();
- if (onClickListener != null) {
- onClickListener.onClick(mTextView);
- } else {
- final Intent intent = textClassification.getIntent();
- if (intent != null) {
- TextClassification.createStartActivityOnClickListener(
- mTextView.getContext(), intent)
- .onClick(mTextView);
- }
- }
- mMetricsLogger.action(
- MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST,
- textClassification.getLogType());
- stopTextActionMode();
+ if (item.getGroupId() == TextView.ID_ASSIST && onAssistMenuItemClicked(item)) {
return true;
}
return mTextView.onTextContextMenuItem(item.getItemId());
@@ -4040,6 +4100,8 @@ public class Editor {
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.hide();
}
+
+ mAssistClickHandlers.clear();
}
@Override
@@ -4550,17 +4612,15 @@ public class Editor {
final Layout layout = mTextView.getLayout();
final int lineNumber = layout.getLineForOffset(offset);
// Horizontally snap to character offset.
- final float xPosInView = getHorizontal(mTextView.getLayout(), offset);
+ final float xPosInView = getHorizontal(mTextView.getLayout(), offset)
+ + mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
// Vertically snap to middle of current line.
final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
- + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f;
- final int[] coordinatesOnScreen = new int[2];
- mTextView.getLocationOnScreen(coordinatesOnScreen);
- final float centerXOnScreen = mTextView.convertViewToScreenCoord(xPosInView, true);
- final float centerYOnScreen = mTextView.convertViewToScreenCoord(yPosInView, false);
+ + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
+ + mTextView.getTotalPaddingTop() - mTextView.getScrollY();
suspendBlink();
- mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM);
+ mMagnifier.show(xPosInView, yPosInView);
}
protected final void dismissMagnifier() {
@@ -6573,6 +6633,9 @@ public class Editor {
private void loadSupportedActivities() {
mSupportedActivities.clear();
+ if (!mContext.canStartActivityForResult()) {
+ return;
+ }
PackageManager packageManager = mTextView.getContext().getPackageManager();
List<ResolveInfo> unfiltered =
packageManager.queryIntentActivities(createProcessTextIntent(), 0);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ce805526e4ad..d9bc51fffd6a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9219,36 +9219,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- @Override
- public void invalidate() {
- super.invalidate();
-
- if (mEditor != null) {
- mEditor.invalidateMagnifier();
- }
- }
-
- @Override
- public void invalidate(int l, int t, int r, int b) {
- super.invalidate(l, t, r, b);
-
- if (mEditor != null) {
- mEditor.invalidateMagnifier(
- convertViewToScreenCoord(l, true /* isHorizontal */),
- convertViewToScreenCoord(t, false /* isHorizontal */),
- convertViewToScreenCoord(r, true /* isHorizontal */),
- convertViewToScreenCoord(b, false /* isHorizontal */));
- }
- }
-
- float convertViewToScreenCoord(float viewCoord, boolean isHorizontal) {
- final int[] coordinatesOnScreen = new int[2];
- getLocationOnScreen(coordinatesOnScreen);
- return isHorizontal
- ? viewCoord + getTotalPaddingLeft() - getScrollX() + coordinatesOnScreen[0]
- : viewCoord + getTotalPaddingTop() - getScrollY() + coordinatesOnScreen[1];
- }
-
/**
* @return whether or not the cursor is visible (assuming this TextView is editable)
*
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 239d7091ed3d..f0d05da2ec5e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -63,6 +63,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
@@ -4030,6 +4031,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
historyName, uid);
+ StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 1);
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
@@ -4045,6 +4047,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
historyName, uid);
+ StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, name, historyName, 0);
}
void aggregateLastWakeupUptimeLocked(long uptimeMs) {
@@ -4052,6 +4055,7 @@ public class BatteryStatsImpl extends BatteryStats {
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptime * 1000, 1); // time in in microseconds
+ StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000);
mLastWakeupReason = null;
}
}
@@ -4403,6 +4407,7 @@ public class BatteryStatsImpl extends BatteryStats {
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
+ StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0);
}
}
@@ -4629,6 +4634,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
+ StatsLog.write(StatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
} else {
stopAllPhoneSignalStrengthTimersLocked(-1);
}
@@ -5184,6 +5190,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (strengthBin >= 0) {
if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
+ StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
}
mHistoryCur.states2 =
(mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
@@ -6049,6 +6056,8 @@ public class BatteryStatsImpl extends BatteryStats {
mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase);
}
mFullWifiLockTimer.startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, getUid(), 1);
}
}
@@ -6057,6 +6066,10 @@ public class BatteryStatsImpl extends BatteryStats {
if (mFullWifiLockOut) {
mFullWifiLockOut = false;
mFullWifiLockTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mFullWifiLockTimer.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, getUid(), 0);
+ }
}
}
@@ -6070,6 +6083,8 @@ public class BatteryStatsImpl extends BatteryStats {
mOnBatteryBackgroundTimeBase);
}
mWifiScanTimer.startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, getUid(), 1);
}
}
@@ -6078,6 +6093,10 @@ public class BatteryStatsImpl extends BatteryStats {
if (mWifiScanStarted) {
mWifiScanStarted = false;
mWifiScanTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mWifiScanTimer.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, getUid(), 0);
+ }
}
}
@@ -6180,17 +6199,25 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 1);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 0);
+ }
}
}
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, getUid(), 0);
}
}
@@ -6204,17 +6231,25 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 1);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 0);
+ }
}
}
public void noteResetVideoLocked(long elapsedRealtimeMs) {
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), 0);
}
}
@@ -6228,17 +6263,25 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 1);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 0);
+ }
}
}
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), 0);
}
}
@@ -6252,17 +6295,25 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 1);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 0);
+ }
}
}
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.CAMERA_STATE_CHANGED, getUid(), 0);
}
}
@@ -6311,26 +6362,42 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothScanStartedLocked(long elapsedRealtimeMs, boolean isUnoptimized) {
createBluetoothScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 1);
if (isUnoptimized) {
createBluetoothUnoptimizedScanTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 1);
}
}
public void noteBluetoothScanStoppedLocked(long elapsedRealtimeMs, boolean isUnoptimized) {
if (mBluetoothScanTimer != null) {
mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mBluetoothScanTimer.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 0);
+ }
}
if (isUnoptimized && mBluetoothUnoptimizedScanTimer != null) {
mBluetoothUnoptimizedScanTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mBluetoothUnoptimizedScanTimer.isRunningLocked()) {
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 0);
+ }
}
}
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs) {
if (mBluetoothScanTimer != null) {
mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, getUid(), 0);
}
if (mBluetoothUnoptimizedScanTimer != null) {
mBluetoothUnoptimizedScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, getUid(), 0);
}
}
@@ -6352,6 +6419,9 @@ public class BatteryStatsImpl extends BatteryStats {
createBluetoothScanResultCounterLocked().addAtomic(numNewResults);
// Uses background timebase, so the count will only be incremented if uid in background.
createBluetoothScanResultBgCounterLocked().addAtomic(numNewResults);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ // TODO(statsd): This could be in AppScanStats instead, if desired.
+ StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, getUid(), numNewResults);
}
@Override
@@ -8725,6 +8795,8 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.SYNC_STATE_CHANGED, getUid(), name, 1);
}
}
@@ -8732,6 +8804,10 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = mSyncStats.stopObject(name);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
+ if (!t.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.SYNC_STATE_CHANGED, getUid(), name, 0);
+ }
}
}
@@ -8739,6 +8815,8 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = mJobStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), name, 1);
}
}
@@ -8746,6 +8824,10 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = mJobStats.stopObject(name);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
+ if (!t.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), name, 0);
+ }
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
SparseIntArray types = mJobCompletions.get(name);
@@ -8809,9 +8891,13 @@ public class BatteryStatsImpl extends BatteryStats {
Wakelock wl = mWakelockStats.startObject(name);
if (wl != null) {
getWakelockTimerLocked(wl, type).startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Hopefully use a worksource instead of a uid (so move elsewhere)
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, getUid(), type, name, 1);
}
if (type == WAKE_TYPE_PARTIAL) {
createAggregatedPartialWakelockTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.UID_WAKELOCK_STATE_CHANGED, getUid(), type, 1);
if (pid >= 0) {
Pid p = getPidStatsLocked(pid);
if (p.mWakeNesting++ == 0) {
@@ -8824,11 +8910,21 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStopWakeLocked(int pid, String name, int type, long elapsedRealtimeMs) {
Wakelock wl = mWakelockStats.stopObject(name);
if (wl != null) {
- getWakelockTimerLocked(wl, type).stopRunningLocked(elapsedRealtimeMs);
+ StopwatchTimer wlt = getWakelockTimerLocked(wl, type);
+ wlt.stopRunningLocked(elapsedRealtimeMs);
+ if (!wlt.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, getUid(), type, name, 0);
+ }
}
if (type == WAKE_TYPE_PARTIAL) {
if (mAggregatedPartialWakelockTimer != null) {
mAggregatedPartialWakelockTimer.stopRunningLocked(elapsedRealtimeMs);
+ if (!mAggregatedPartialWakelockTimer.isRunningLocked()) {
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ StatsLog.write(StatsLog.UID_WAKELOCK_STATE_CHANGED, getUid(), type,
+ 0);
+ }
}
if (pid >= 0) {
Pid p = mPids.get(pid);
@@ -8852,6 +8948,12 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
t.startRunningLocked(elapsedRealtimeMs);
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ if (sensor == Sensor.GPS) {
+ StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), 1);
+ } else {
+ StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, getUid(), sensor, 1);
+ }
}
public void noteStopSensor(int sensor, long elapsedRealtimeMs) {
@@ -8859,6 +8961,14 @@ public class BatteryStatsImpl extends BatteryStats {
DualTimer t = getSensorTimerLocked(sensor, false);
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
+ if (!t.isRunningLocked()) { // only tell statsd if truly stopped
+ // TODO(statsd): Possibly use a worksource instead of a uid.
+ if (sensor == Sensor.GPS) {
+ StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, getUid(), 0);
+ } else {
+ StatsLog.write(StatsLog.SENSOR_STATE_CHANGED, getUid(), sensor, 0);
+ }
+ }
}
}
@@ -11068,11 +11178,15 @@ public class BatteryStatsImpl extends BatteryStats {
// This should probably be exposed in the API, though it's not critical
public static final int BATTERY_PLUGGED_NONE = 0;
- public void setBatteryStateLocked(int status, int health, int plugType, int level,
- int temp, int volt, int chargeUAh, int chargeFullUAh) {
+ public void setBatteryStateLocked(final int status, final int health, final int plugType,
+ final int level, /* not final */ int temp, final int volt, final int chargeUAh,
+ final int chargeFullUAh) {
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
+ reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
+ status, plugType, level, temp);
+
final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
final long uptime = mClocks.uptimeMillis();
final long elapsedRealtime = mClocks.elapsedRealtime();
@@ -11249,6 +11363,24 @@ public class BatteryStatsImpl extends BatteryStats {
mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
}
+ // Inform StatsLog of setBatteryState changes.
+ // If this is the first reporting, pass in recentPast == null.
+ private void reportChangesToStatsLog(HistoryItem recentPast,
+ final int status, final int plugType, final int level, final int temp) {
+
+ if (recentPast == null || recentPast.batteryStatus != status) {
+ StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, status);
+ }
+ if (recentPast == null || recentPast.batteryPlugType != plugType) {
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, plugType);
+ }
+ if (recentPast == null || recentPast.batteryLevel != level) {
+ StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, level);
+ }
+ // Let's just always print the temperature, regardless of whether it changed.
+ StatsLog.write(StatsLog.DEVICE_TEMPERATURE_REPORTED, temp);
+ }
+
public long getAwakeTimeBattery() {
return computeBatteryUptime(getBatteryUptimeLocked(), STATS_CURRENT);
}
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index ea4575aba9c0..5bddd2f98983 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -16,9 +16,15 @@
package com.android.internal.os;
+import android.annotation.NonNull;
+import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.util.Preconditions;
import dalvik.system.VMRuntime;
@@ -31,11 +37,14 @@ import java.util.ArrayList;
* @see IBinder
*/
public class BinderInternal {
+ private static final String TAG = "BinderInternal";
static WeakReference<GcWatcher> sGcWatcher
= new WeakReference<GcWatcher>(new GcWatcher());
static ArrayList<Runnable> sGcWatchers = new ArrayList<>();
static Runnable[] sTmpWatchers = new Runnable[1];
static long sLastGcTime;
+ static final BinderProxyLimitListenerDelegate sBinderProxyLimitListenerDelegate =
+ new BinderProxyLimitListenerDelegate();
static final class GcWatcher {
@Override
@@ -106,4 +115,96 @@ public class BinderInternal {
static void forceBinderGc() {
forceGc("Binder");
}
+
+ /**
+ * Enable/disable Binder Proxy Instance Counting by Uid. While enabled, the set callback will
+ * be called if this process holds too many Binder Proxies on behalf of a Uid.
+ * @param enabled true to enable counting, false to disable
+ */
+ public static final native void nSetBinderProxyCountEnabled(boolean enabled);
+
+ /**
+ * Get the current number of Binder Proxies held for each uid.
+ * @return SparseIntArray mapping uids to the number of Binder Proxies currently held
+ */
+ public static final native SparseIntArray nGetBinderProxyPerUidCounts();
+
+ /**
+ * Get the current number of Binder Proxies held for an individual uid.
+ * @param uid Requested uid for Binder Proxy count
+ * @return int with the number of Binder proxies held for a uid
+ */
+ public static final native int nGetBinderProxyCount(int uid);
+
+ /**
+ * Set the Binder Proxy watermarks. Default high watermark = 2500. Default low watermark = 2000
+ * @param high The limit at which the BinderProxyListener callback will be called.
+ * @param low The threshold a binder count must drop below before the callback
+ * can be called again. (This is to avoid many repeated calls to the
+ * callback in a brief period of time)
+ */
+ public static final native void nSetBinderProxyCountWatermarks(int high, int low);
+
+ /**
+ * Interface for callback invocation when the Binder Proxy limit is reached. onLimitReached will
+ * be called with the uid of the app causing too many Binder Proxies
+ */
+ public interface BinderProxyLimitListener {
+ public void onLimitReached(int uid);
+ }
+
+ /**
+ * Callback used by native code to trigger a callback in java code. The callback will be
+ * triggered when too many binder proxies from a uid hits the allowed limit.
+ * @param uid The uid of the bad behaving app sending too many binders
+ */
+ public static void binderProxyLimitCallbackFromNative(int uid) {
+ sBinderProxyLimitListenerDelegate.notifyClient(uid);
+ }
+
+ /**
+ * Set a callback to be triggered when a uid's Binder Proxy limit is reached for this process.
+ * @param listener OnLimitReached of listener will be called in the thread provided by handler
+ * @param handler must not be null, callback will be posted through the handler;
+ *
+ */
+ public static void setBinderProxyCountCallback(BinderProxyLimitListener listener,
+ @NonNull Handler handler) {
+ Preconditions.checkNotNull(handler,
+ "Must provide NonNull Handler to setBinderProxyCountCallback when setting "
+ + "BinderProxyLimitListener");
+ sBinderProxyLimitListenerDelegate.setListener(listener, handler);
+ }
+
+ /**
+ * Clear the Binder Proxy callback
+ */
+ public static void clearBinderProxyCountCallback() {
+ sBinderProxyLimitListenerDelegate.setListener(null, null);
+ }
+
+ static private class BinderProxyLimitListenerDelegate {
+ private BinderProxyLimitListener mBinderProxyLimitListener;
+ private Handler mHandler;
+
+ void setListener(BinderProxyLimitListener listener, Handler handler) {
+ synchronized (this) {
+ mBinderProxyLimitListener = listener;
+ mHandler = handler;
+ }
+ }
+
+ void notifyClient(final int uid) {
+ synchronized (this) {
+ if (mBinderProxyLimitListener != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mBinderProxyLimitListener.onLimitReached(uid);
+ }
+ });
+ }
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/util/LocalLog.java b/core/java/com/android/internal/util/LocalLog.java
index f0e6171562f9..8edb739f273c 100644
--- a/core/java/com/android/internal/util/LocalLog.java
+++ b/core/java/com/android/internal/util/LocalLog.java
@@ -20,6 +20,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
/**
* Helper class for logging serious issues, which also keeps a small
@@ -63,4 +64,16 @@ public class LocalLog {
return true;
}
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ synchronized (mLines) {
+ for (int i = 0; i < mLines.size(); ++i) {
+ proto.write(LocalLogProto.LINES, mLines.get(i));
+ }
+ }
+
+ proto.end(token);
+ }
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 2098ebd8814a..5b65bbe1e90e 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -36,6 +36,7 @@ import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicBoolean;
public class InputConnectionWrapper implements InputConnection {
private static final int MAX_WAIT_TIME_MILLIS = 2000;
@@ -46,6 +47,14 @@ public class InputConnectionWrapper implements InputConnection {
@MissingMethodFlags
private final int mMissingMethods;
+ /**
+ * {@code true} if the system already decided to take away IME focus from the target app. This
+ * can be signaled even when the corresponding signal is in the task queue and
+ * {@link InputMethodService#onUnbindInput()} is not yet called back on the UI thread.
+ */
+ @NonNull
+ private final AtomicBoolean mIsUnbindIssued;
+
static class InputContextCallback extends IInputContextCallback.Stub {
private static final String TAG = "InputConnectionWrapper.ICC";
public int mSeq;
@@ -231,14 +240,20 @@ public class InputConnectionWrapper implements InputConnection {
public InputConnectionWrapper(
@NonNull WeakReference<AbstractInputMethodService> inputMethodService,
- IInputContext inputContext, @MissingMethodFlags final int missingMethods) {
+ IInputContext inputContext, @MissingMethodFlags final int missingMethods,
+ @NonNull AtomicBoolean isUnbindIssued) {
mInputMethodService = inputMethodService;
mIInputContext = inputContext;
mMissingMethods = missingMethods;
+ mIsUnbindIssued = isUnbindIssued;
}
@AnyThread
public CharSequence getTextAfterCursor(int length, int flags) {
+ if (mIsUnbindIssued.get()) {
+ return null;
+ }
+
CharSequence value = null;
try {
InputContextCallback callback = InputContextCallback.getInstance();
@@ -258,6 +273,10 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public CharSequence getTextBeforeCursor(int length, int flags) {
+ if (mIsUnbindIssued.get()) {
+ return null;
+ }
+
CharSequence value = null;
try {
InputContextCallback callback = InputContextCallback.getInstance();
@@ -277,6 +296,10 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public CharSequence getSelectedText(int flags) {
+ if (mIsUnbindIssued.get()) {
+ return null;
+ }
+
if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) {
// This method is not implemented.
return null;
@@ -300,6 +323,10 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public int getCursorCapsMode(int reqModes) {
+ if (mIsUnbindIssued.get()) {
+ return 0;
+ }
+
int value = 0;
try {
InputContextCallback callback = InputContextCallback.getInstance();
@@ -319,6 +346,10 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ if (mIsUnbindIssued.get()) {
+ return null;
+ }
+
ExtractedText value = null;
try {
InputContextCallback callback = InputContextCallback.getInstance();
@@ -516,6 +547,10 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public boolean requestCursorUpdates(int cursorUpdateMode) {
+ if (mIsUnbindIssued.get()) {
+ return false;
+ }
+
boolean result = false;
if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
// This method is not implemented.
@@ -550,6 +585,10 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ if (mIsUnbindIssued.get()) {
+ return false;
+ }
+
boolean result = false;
if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
// This method is not implemented.
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
index 9bc0778d9be6..f6741c3b5169 100644
--- a/core/java/com/android/internal/widget/Magnifier.java
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -22,9 +22,7 @@ import android.annotation.UiThread;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Handler;
import android.util.Log;
import android.view.Gravity;
@@ -38,13 +36,15 @@ import android.widget.PopupWindow;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
+import java.util.Timer;
+import java.util.TimerTask;
+
/**
* Android magnifier widget. Can be used by any view which is attached to window.
*/
public final class Magnifier {
private static final String LOG_TAG = "magnifier";
- // Use this to specify that a previous configuration value does not exist.
- private static final int INEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+ private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps
// The view for which this magnifier is attached.
private final View mView;
// The window containing the magnifier.
@@ -62,15 +62,10 @@ public final class Magnifier {
// The callback of the pixel copy request will be invoked on this Handler when
// the copy is finished.
private final Handler mPixelCopyHandler = Handler.getMain();
-
- private RectF mTmpRectF;
-
- // Variables holding previous states, used for detecting redundant calls and invalidation.
- private Point mPrevStartCoordsOnScreen = new Point(
- INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
- private PointF mPrevCenterCoordsOnScreen = new PointF(
- INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE);
- private float mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
+ // Current magnification scale.
+ private final float mZoomScale;
+ // Timer used to schedule the copy task.
+ private Timer mTimer;
/**
* Initializes a magnifier.
@@ -81,10 +76,12 @@ public final class Magnifier {
public Magnifier(@NonNull View view) {
mView = Preconditions.checkNotNull(view);
final Context context = mView.getContext();
+ final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation);
final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null);
+ content.findViewById(R.id.magnifier_inner).setClipToOutline(true);
mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width);
mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height);
- final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation);
+ mZoomScale = context.getResources().getFloat(R.dimen.magnifier_zoom_scale);
mWindow = new PopupWindow(context);
mWindow.setContentView(content);
@@ -94,52 +91,40 @@ public final class Magnifier {
mWindow.setTouchable(false);
mWindow.setBackgroundDrawable(null);
- mBitmap = Bitmap.createBitmap(mWindowWidth, mWindowHeight, Bitmap.Config.ARGB_8888);
+ final int bitmapWidth = (int) (mWindowWidth / mZoomScale);
+ final int bitmapHeight = (int) (mWindowHeight / mZoomScale);
+ mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
getImageView().setImageBitmap(mBitmap);
}
/**
* Shows the magnifier on the screen.
*
- * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source. The
- * lower end is clamped to 0
- * @param centerYOnScreen vertical coordinate of the center point of the magnifier source. The
- * lower end is clamped to 0
- * @param scale the scale at which the magnifier zooms on the source content. The
- * lower end is clamped to 1 and the higher end to 4
+ * @param xPosInView horizontal coordinate of the center point of the magnifier source relative
+ * to the view. The lower end is clamped to 0
+ * @param yPosInView vertical coordinate of the center point of the magnifier source
+ * relative to the view. The lower end is clamped to 0
*/
- public void show(@FloatRange(from=0) float centerXOnScreen,
- @FloatRange(from=0) float centerYOnScreen,
- @FloatRange(from=1, to=4) float scale) {
- if (scale > 4) {
- scale = 4;
- }
-
- if (scale < 1) {
- scale = 1;
- }
-
- if (centerXOnScreen < 0) {
- centerXOnScreen = 0;
+ public void show(@FloatRange(from=0) float xPosInView, @FloatRange(from=0) float yPosInView) {
+ if (xPosInView < 0) {
+ xPosInView = 0;
}
- if (centerYOnScreen < 0) {
- centerYOnScreen = 0;
+ if (yPosInView < 0) {
+ yPosInView = 0;
}
- showInternal(centerXOnScreen, centerYOnScreen, scale, false);
- }
+ configureCoordinates(xPosInView, yPosInView);
- private void showInternal(@FloatRange(from=0) float centerXOnScreen,
- @FloatRange(from=0) float centerYOnScreen,
- @FloatRange(from=1, to=4) float scale,
- boolean forceShow) {
- if (mPrevScale != scale) {
- resizeBitmap(scale);
- mPrevScale = scale;
+ if (mTimer == null) {
+ mTimer = new Timer();
+ mTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ performPixelCopy();
+ }
+ }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS);
}
- configureCoordinates(centerXOnScreen, centerYOnScreen);
- maybePerformPixelCopy(scale, forceShow);
if (mWindow.isShowing()) {
mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
@@ -148,9 +133,6 @@ public final class Magnifier {
mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
mWindowCoords.x, mWindowCoords.y);
}
-
- mPrevCenterCoordsOnScreen.x = centerXOnScreen;
- mPrevCenterCoordsOnScreen.y = centerYOnScreen;
}
/**
@@ -159,36 +141,10 @@ public final class Magnifier {
public void dismiss() {
mWindow.dismiss();
- mPrevStartCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevStartCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevCenterCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevCenterCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE;
- }
-
- /**
- * Forces the magnifier to update content by taking and showing a new snapshot using the
- * previous coordinates. It does this only if the magnifier is showing and the dirty rectangle
- * intersects the rectangle which holds the content to be magnified.
- *
- * @param dirtyRectOnScreen the rectangle representing the screen bounds of the dirty region
- */
- public void invalidate(RectF dirtyRectOnScreen) {
- if (mWindow.isShowing() && mPrevCenterCoordsOnScreen.x != INEXISTENT_PREVIOUS_CONFIG_VALUE
- && mPrevCenterCoordsOnScreen.y != INEXISTENT_PREVIOUS_CONFIG_VALUE
- && mPrevScale != INEXISTENT_PREVIOUS_CONFIG_VALUE) {
- // Update the current showing RectF.
- mTmpRectF = new RectF(mPrevStartCoordsOnScreen.x,
- mPrevStartCoordsOnScreen.y,
- mPrevStartCoordsOnScreen.x + mBitmap.getWidth(),
- mPrevStartCoordsOnScreen.y + mBitmap.getHeight());
-
- // Update only if we are currently showing content that has been declared as invalid.
- if (RectF.intersects(dirtyRectOnScreen, mTmpRectF)) {
- // Update the contents shown in the magnifier.
- showInternal(mPrevCenterCoordsOnScreen.x, mPrevCenterCoordsOnScreen.y, mPrevScale,
- true /* forceShow */);
- }
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer.purge();
+ mTimer = null;
}
}
@@ -206,14 +162,19 @@ public final class Magnifier {
return mWindowWidth;
}
- private void resizeBitmap(float scale) {
- final int bitmapWidth = (int) (mWindowWidth / scale);
- final int bitmapHeight = (int) (mWindowHeight / scale);
- mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
- getImageView().setImageBitmap(mBitmap);
+ /**
+ * @return the zoom scale of the magnifier.
+ */
+ public float getZoomScale() {
+ return mZoomScale;
}
- private void configureCoordinates(float posXOnScreen, float posYOnScreen) {
+ private void configureCoordinates(float xPosInView, float yPosInView) {
+ final int[] coordinatesOnScreen = new int[2];
+ mView.getLocationOnScreen(coordinatesOnScreen);
+ final float posXOnScreen = xPosInView + coordinatesOnScreen[0];
+ final float posYOnScreen = yPosInView + coordinatesOnScreen[1];
+
mCenterZoomCoords.x = (int) posXOnScreen;
mCenterZoomCoords.y = (int) posYOnScreen;
@@ -223,7 +184,7 @@ public final class Magnifier {
mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
}
- private void maybePerformPixelCopy(final float scale, final boolean forceShow) {
+ private void performPixelCopy() {
final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
@@ -234,13 +195,6 @@ public final class Magnifier {
rawStartX = mView.getWidth() - mBitmap.getWidth();
}
- if (!forceShow && rawStartX == mPrevStartCoordsOnScreen.x
- && startY == mPrevStartCoordsOnScreen.y
- && scale == mPrevScale) {
- // Skip, we are already showing the desired content.
- return;
- }
-
final int startX = rawStartX;
final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
@@ -251,11 +205,7 @@ public final class Magnifier {
new Rect(startX, startY, startX + mBitmap.getWidth(),
startY + mBitmap.getHeight()),
mBitmap,
- result -> {
- getImageView().invalidate();
- mPrevStartCoordsOnScreen.x = startX;
- mPrevStartCoordsOnScreen.y = startY;
- },
+ result -> getImageView().invalidate(),
mPixelCopyHandler);
} else {
Log.d(LOG_TAG, "Could not perform PixelCopy request");
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index c6801bf613fa..9e6985ca300e 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -56,9 +56,9 @@ static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring langs, j
if (langs != nullptr) {
ScopedUtfChars str(env, langs);
builder = new NativeFamilyBuilder(
- minikin::FontStyle::registerLanguageList(str.c_str()), variant);
+ minikin::FontStyle::registerLocaleList(str.c_str()), variant);
} else {
- builder = new NativeFamilyBuilder(minikin::FontStyle::registerLanguageList(""), variant);
+ builder = new NativeFamilyBuilder(minikin::FontStyle::registerLocaleList(""), variant);
}
return reinterpret_cast<jlong>(builder);
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 93bd16b2d08a..fd62a19f310f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -544,7 +544,7 @@ namespace PaintGlue {
static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
ScopedUtfChars localesChars(env, locales);
- jint minikinLangListId = minikin::FontStyle::registerLanguageList(localesChars.c_str());
+ jint minikinLangListId = minikin::FontStyle::registerLocaleList(localesChars.c_str());
obj->setMinikinLangListId(minikinLangListId);
return minikinLangListId;
}
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index c3f9bf7f25bf..1efff7f8b0fb 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -765,6 +765,14 @@ static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
if (startPos > totalRows) {
ALOGE("startPos %d > actual rows %d", startPos, totalRows);
}
+ if (totalRows > 0 && addedRows == 0) {
+ String8 msg;
+ msg.appendFormat("Row too big to fit into CursorWindow requiredPos=%d, totalRows=%d",
+ requiredPos, totalRows);
+ throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.string());
+ return 0;
+ }
+
jlong result = jlong(startPos) << 32 | jlong(totalRows);
return result;
}
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index fe14d483743f..c4f22eeb8b95 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -35,6 +35,7 @@
#include <hidl/HidlTransportSupport.h>
#include <hwbinder/ProcessState.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
#include <vintf/parse_string.h>
#include <utils/misc.h>
@@ -261,14 +262,9 @@ static void JHwBinder_native_registerService(
JNIEnv *env,
jobject thiz,
jstring serviceNameObj) {
- if (serviceNameObj == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return;
- }
-
- const char *serviceName = env->GetStringUTFChars(serviceNameObj, NULL);
- if (serviceName == NULL) {
- return; // XXX exception already pending?
+ ScopedUtfChars str(env, serviceNameObj);
+ if (str.c_str() == nullptr) {
+ return; // NPE will be pending.
}
sp<hardware::IBinder> binder = JHwBinder::GetNativeBinder(env, thiz);
@@ -284,15 +280,12 @@ static void JHwBinder_native_registerService(
return;
}
- Return<bool> ret = manager->add(serviceName, base);
-
- env->ReleaseStringUTFChars(serviceNameObj, serviceName);
- serviceName = NULL;
+ Return<bool> ret = manager->add(str.c_str(), base);
bool ok = ret.isOk() && ret;
if (ok) {
- LOG(INFO) << "Starting thread pool.";
+ LOG(INFO) << "HwBinder: Starting thread pool for " << str.c_str();
::android::hardware::ProcessState::self()->startThreadPool();
}
@@ -308,31 +301,26 @@ static jobject JHwBinder_native_getService(
using ::android::hidl::base::V1_0::IBase;
using ::android::hardware::details::getRawServiceInternal;
- if (ifaceNameObj == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return NULL;
- }
- if (serviceNameObj == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return NULL;
- }
-
- const char *ifaceNameCStr = env->GetStringUTFChars(ifaceNameObj, NULL);
- if (ifaceNameCStr == NULL) {
- return NULL; // XXX exception already pending?
+ std::string ifaceName;
+ {
+ ScopedUtfChars str(env, ifaceNameObj);
+ if (str.c_str() == nullptr) {
+ return nullptr; // NPE will be pending.
+ }
+ ifaceName = str.c_str();
}
- std::string ifaceName(ifaceNameCStr);
- env->ReleaseStringUTFChars(ifaceNameObj, ifaceNameCStr);
- const char *serviceNameCStr = env->GetStringUTFChars(serviceNameObj, NULL);
- if (serviceNameCStr == NULL) {
- return NULL; // XXX exception already pending?
+ std::string serviceName;
+ {
+ ScopedUtfChars str(env, serviceNameObj);
+ if (str.c_str() == nullptr) {
+ return nullptr; // NPE will be pending.
+ }
+ serviceName = str.c_str();
}
- std::string serviceName(serviceNameCStr);
- env->ReleaseStringUTFChars(serviceNameObj, serviceNameCStr);
// TODO(b/67981006): true /* retry */
- sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, false /* retry */, false /* getStub */);
+ sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, false /* retry */, false /* getStub */);
sp<hardware::IBinder> service = hardware::toBinder<hidl::base::V1_0::IBase>(ret);
if (service == NULL) {
@@ -340,13 +328,14 @@ static jobject JHwBinder_native_getService(
return NULL;
}
- LOG(INFO) << "Starting thread pool.";
+ LOG(INFO) << "HwBinder: Starting thread pool for " << serviceName << "::" << ifaceName;
::android::hardware::ProcessState::self()->startThreadPool();
return JHwRemoteBinder::NewObject(env, service);
}
-void JHwBinder_native_configureRpcThreadpool(jlong maxThreads, jboolean callerWillJoin) {
+void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass,
+ jlong maxThreads, jboolean callerWillJoin) {
CHECK(maxThreads > 0);
ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/);
}
@@ -355,7 +344,7 @@ void JHwBinder_native_joinRpcThreadpool() {
IPCThreadState::self()->joinThreadPool();
}
-static void JHwBinder_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/)
+static void JHwBinder_report_sysprop_change(JNIEnv * /*env*/, jclass /*clazz*/)
{
report_sysprop_change();
}
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 05bec28a5d39..6f9cc22fb3ab 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -82,45 +82,45 @@ static void init() {
constexpr int INDIC_MIN_PREFIX = 2;
constexpr int INDIC_MIN_SUFFIX = 2;
- addHyphenator("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Assamese
- addHyphenator("be", 2, 2); // Belarusian
- addHyphenator("bg", 2, 2); // Bulgarian
- addHyphenator("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Bengali
- addHyphenator("cu", 1, 2); // Church Slavonic
- addHyphenator("cy", 2, 3); // Welsh
- addHyphenator("da", 2, 2); // Danish
- addHyphenator("de-1901", 2, 2); // German 1901 orthography
- addHyphenator("de-1996", 2, 2); // German 1996 orthography
- addHyphenator("de-CH-1901", 2, 2); // Swiss High German 1901 orthography
- addHyphenator("en-GB", 2, 3); // British English
- addHyphenator("en-US", 2, 3); // American English
- addHyphenator("es", 2, 2); // Spanish
- addHyphenator("et", 2, 3); // Estonian
- addHyphenator("eu", 2, 2); // Basque
- addHyphenator("fr", 2, 3); // French
- addHyphenator("ga", 2, 3); // Irish
- addHyphenator("gu", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Gujarati
- addHyphenator("hi", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Hindi
- addHyphenator("hr", 2, 2); // Croatian
- addHyphenator("hu", 2, 2); // Hungarian
+ addHyphenator("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Assamese
+ addHyphenator("be", 2, 2); // Belarusian
+ addHyphenator("bg", 2, 2); // Bulgarian
+ addHyphenator("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Bengali
+ addHyphenator("cu", 1, 2); // Church Slavonic
+ addHyphenator("cy", 2, 3); // Welsh
+ addHyphenator("da", 2, 2); // Danish
+ addHyphenator("de-1901", 2, 2); // German 1901 orthography
+ addHyphenator("de-1996", 2, 2); // German 1996 orthography
+ addHyphenator("de-CH-1901", 2, 2); // Swiss High German 1901 orthography
+ addHyphenator("en-GB", 2, 3); // British English
+ addHyphenator("en-US", 2, 3); // American English
+ addHyphenator("es", 2, 2); // Spanish
+ addHyphenator("et", 2, 3); // Estonian
+ addHyphenator("eu", 2, 2); // Basque
+ addHyphenator("fr", 2, 3); // French
+ addHyphenator("ga", 2, 3); // Irish
+ addHyphenator("gu", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Gujarati
+ addHyphenator("hi", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Hindi
+ addHyphenator("hr", 2, 2); // Croatian
+ addHyphenator("hu", 2, 2); // Hungarian
// texhyphen sources say Armenian may be (1, 2); but that it needs confirmation.
// Going with a more conservative value of (2, 2) for now.
- addHyphenator("hy", 2, 2); // Armenian
- addHyphenator("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Kannada
- addHyphenator("la", 2, 2); // Latin
- addHyphenator("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Malayalam
- addHyphenator("mn-Cyrl", 2, 2); // Mongolian in Cyrillic script
- addHyphenator("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Marathi
- addHyphenator("nb", 2, 2); // Norwegian Bokmål
- addHyphenator("nn", 2, 2); // Norwegian Nynorsk
- addHyphenator("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Oriya
- addHyphenator("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Punjabi
- addHyphenator("pt", 2, 3); // Portuguese
- addHyphenator("sl", 2, 2); // Slovenian
- addHyphenator("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Tamil
- addHyphenator("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Telugu
- addHyphenator("tk", 2, 2); // Turkmen
- addHyphenator("und-Ethi", 1, 1); // Any language in Ethiopic script
+ addHyphenator("hy", 2, 2); // Armenian
+ addHyphenator("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Kannada
+ addHyphenator("la", 2, 2); // Latin
+ addHyphenator("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Malayalam
+ addHyphenator("mn-Cyrl", 2, 2); // Mongolian in Cyrillic script
+ addHyphenator("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Marathi
+ addHyphenator("nb", 2, 2); // Norwegian Bokmål
+ addHyphenator("nn", 2, 2); // Norwegian Nynorsk
+ addHyphenator("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Oriya
+ addHyphenator("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Punjabi
+ addHyphenator("pt", 2, 3); // Portuguese
+ addHyphenator("sl", 2, 2); // Slovenian
+ addHyphenator("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Tamil
+ addHyphenator("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Telugu
+ addHyphenator("tk", 2, 2); // Turkmen
+ addHyphenator("und-Ethi", 1, 1); // Any language in Ethiopic script
// Following two hyphenators do not have pattern files but there is some special logic based on
// language.
@@ -130,13 +130,13 @@ static void init() {
// English locales that fall back to en-US. The data is from CLDR. It's all English locales,
// minus the locales whose parent is en-001 (from supplementalData.xml, under <parentLocales>).
// TODO: Figure out how to get this from ICU.
- addHyphenatorAlias("en-AS", "en-US"); // English (American Samoa)
- addHyphenatorAlias("en-GU", "en-US"); // English (Guam)
- addHyphenatorAlias("en-MH", "en-US"); // English (Marshall Islands)
- addHyphenatorAlias("en-MP", "en-US"); // English (Northern Mariana Islands)
- addHyphenatorAlias("en-PR", "en-US"); // English (Puerto Rico)
- addHyphenatorAlias("en-UM", "en-US"); // English (United States Minor Outlying Islands)
- addHyphenatorAlias("en-VI", "en-US"); // English (Virgin Islands)
+ addHyphenatorAlias("en-AS", "en-US"); // English (American Samoa)
+ addHyphenatorAlias("en-GU", "en-US"); // English (Guam)
+ addHyphenatorAlias("en-MH", "en-US"); // English (Marshall Islands)
+ addHyphenatorAlias("en-MP", "en-US"); // English (Northern Mariana Islands)
+ addHyphenatorAlias("en-PR", "en-US"); // English (Puerto Rico)
+ addHyphenatorAlias("en-UM", "en-US"); // English (United States Minor Outlying Islands)
+ addHyphenatorAlias("en-VI", "en-US"); // English (Virgin Islands)
// All English locales other than those falling back to en-US are mapped to en-GB.
addHyphenatorAlias("en", "en-GB");
@@ -150,17 +150,28 @@ static void init() {
addHyphenatorAlias("no", "nb");
// Use mn-Cyrl. According to CLDR's likelySubtags.xml, mn is most likely to be mn-Cyrl.
- addHyphenatorAlias("mn", "mn-Cyrl"); // Mongolian
+ addHyphenatorAlias("mn", "mn-Cyrl"); // Mongolian
// Fall back to Ethiopic script for languages likely to be written in Ethiopic.
// Data is from CLDR's likelySubtags.xml.
// TODO: Convert this to a mechanism using ICU4J's ULocale#addLikelySubtags().
- addHyphenatorAlias("am", "und-Ethi"); // Amharic
- addHyphenatorAlias("byn", "und-Ethi"); // Blin
- addHyphenatorAlias("gez", "und-Ethi"); // Geʻez
- addHyphenatorAlias("ti", "und-Ethi"); // Tigrinya
- addHyphenatorAlias("wal", "und-Ethi"); // Wolaytta
-
+ addHyphenatorAlias("am", "und-Ethi"); // Amharic
+ addHyphenatorAlias("byn", "und-Ethi"); // Blin
+ addHyphenatorAlias("gez", "und-Ethi"); // Geʻez
+ addHyphenatorAlias("ti", "und-Ethi"); // Tigrinya
+ addHyphenatorAlias("wal", "und-Ethi"); // Wolaytta
+
+ // Use Hindi as a fallback hyphenator for all languages written in Devanagari, etc. This makes
+ // sense because our Indic patterns are not really linguistic, but script-based.
+ addHyphenatorAlias("und-Beng", "bn"); // Bengali
+ addHyphenatorAlias("und-Deva", "hi"); // Devanagari -> Hindi
+ addHyphenatorAlias("und-Gujr", "gu"); // Gujarati
+ addHyphenatorAlias("und-Guru", "pa"); // Gurmukhi -> Punjabi
+ addHyphenatorAlias("und-Knda", "kn"); // Kannada
+ addHyphenatorAlias("und-Mlym", "ml"); // Malayalam
+ addHyphenatorAlias("und-Orya", "or"); // Oriya
+ addHyphenatorAlias("und-Taml", "ta"); // Tamil
+ addHyphenatorAlias("und-Telu", "te"); // Telugu
}
static const JNINativeMethod gMethods[] = {
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 04e9dfd2706f..c1419ba6c7c6 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -55,11 +55,11 @@ static JLineBreaksID gLineBreaks_fieldID;
class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate {
public:
JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth,
- std::vector<float>&& indents, std::vector<float>&& leftPaddings,
- std::vector<float>&& rightPaddings, int32_t indentsAndPaddingsOffset)
+ const std::vector<float>& indents, const std::vector<float>& leftPaddings,
+ const std::vector<float>& rightPaddings, int32_t indentsAndPaddingsOffset)
: mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
- mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
- mRightPaddings(std::move(rightPaddings)), mOffset(indentsAndPaddingsOffset) {}
+ mIndents(indents), mLeftPaddings(leftPaddings),
+ mRightPaddings(rightPaddings), mOffset(indentsAndPaddingsOffset) {}
float getLineWidth(size_t lineNo) override {
const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount)
@@ -91,9 +91,9 @@ class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate {
const float mFirstWidth;
const int32_t mFirstLineCount;
const float mRestWidth;
- const std::vector<float> mIndents;
- const std::vector<float> mLeftPaddings;
- const std::vector<float> mRightPaddings;
+ const std::vector<float>& mIndents;
+ const std::vector<float>& mLeftPaddings;
+ const std::vector<float>& mRightPaddings;
const int32_t mOffset;
};
@@ -106,32 +106,132 @@ static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray j
}
}
+class Run {
+ public:
+ Run(int32_t start, int32_t end) : mStart(start), mEnd(end) {}
+ virtual ~Run() {}
+
+ virtual void addTo(minikin::LineBreaker* lineBreaker) = 0;
+
+ protected:
+ const int32_t mStart;
+ const int32_t mEnd;
+
+ private:
+ // Forbid copy and assign.
+ Run(const Run&) = delete;
+ void operator=(const Run&) = delete;
+};
+
+class StyleRun : public Run {
+ public:
+ StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
+ std::shared_ptr<minikin::FontCollection>&& collection,
+ minikin::FontStyle&& style, bool isRtl)
+ : Run(start, end), mPaint(std::move(paint)), mCollection(std::move(collection)),
+ mStyle(std::move(style)), mIsRtl(isRtl) {}
+
+ void addTo(minikin::LineBreaker* lineBreaker) override {
+ lineBreaker->addStyleRun(&mPaint, mCollection, mStyle, mStart, mEnd, mIsRtl);
+ }
+
+ private:
+ minikin::MinikinPaint mPaint;
+ std::shared_ptr<minikin::FontCollection> mCollection;
+ minikin::FontStyle mStyle;
+ const bool mIsRtl;
+};
+
+class Replacement : public Run {
+ public:
+ Replacement(int32_t start, int32_t end, float width, uint32_t localeListId)
+ : Run(start, end), mWidth(width), mLocaleListId(localeListId) {}
+
+ void addTo(minikin::LineBreaker* lineBreaker) override {
+ lineBreaker->addReplacement(mStart, mEnd, mWidth, mLocaleListId);
+ }
+
+ private:
+ const float mWidth;
+ const uint32_t mLocaleListId;
+};
+
+class StaticLayoutNative {
+ public:
+ StaticLayoutNative(
+ minikin::BreakStrategy strategy, minikin::HyphenationFrequency frequency,
+ bool isJustified, std::vector<float>&& indents, std::vector<float>&& leftPaddings,
+ std::vector<float>&& rightPaddings)
+ : mStrategy(strategy), mFrequency(frequency), mIsJustified(isJustified),
+ mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
+ mRightPaddings(std::move(rightPaddings)) {}
+
+ void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
+ std::shared_ptr<minikin::FontCollection> collection,
+ minikin::FontStyle&& style, bool isRtl) {
+ mRuns.emplace_back(std::make_unique<StyleRun>(
+ start, end, std::move(paint), std::move(collection), std::move(style), isRtl));
+ }
+
+ void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
+ mRuns.emplace_back(std::make_unique<Replacement>(start, end, width, localeListId));
+ }
+
+ // Only valid while this instance is alive.
+ inline std::unique_ptr<minikin::LineBreaker::LineWidthDelegate> buildLineWidthDelegate(
+ float firstWidth, int32_t firstLineCount, float restWidth,
+ int32_t indentsAndPaddingsOffset) {
+ return std::make_unique<JNILineBreakerLineWidth>(
+ firstWidth, firstLineCount, restWidth, mIndents, mLeftPaddings, mRightPaddings,
+ indentsAndPaddingsOffset);
+ }
+
+ void addRuns(minikin::LineBreaker* lineBreaker) {
+ for (const auto& run : mRuns) {
+ run->addTo(lineBreaker);
+ }
+ }
+
+ void clearRuns() {
+ mRuns.clear();
+ }
+
+ inline minikin::BreakStrategy getStrategy() const { return mStrategy; }
+ inline minikin::HyphenationFrequency getFrequency() const { return mFrequency; }
+ inline bool isJustified() const { return mIsJustified; }
+
+ private:
+ const minikin::BreakStrategy mStrategy;
+ const minikin::HyphenationFrequency mFrequency;
+ const bool mIsJustified;
+ const std::vector<float> mIndents;
+ const std::vector<float> mLeftPaddings;
+ const std::vector<float> mRightPaddings;
+
+ std::vector<std::unique_ptr<Run>> mRuns;
+};
+
+static inline StaticLayoutNative* toNative(jlong ptr) {
+ return reinterpret_cast<StaticLayoutNative*>(ptr);
+}
+
// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
// hyphenFrequency)
-static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
- jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
- jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency,
- jboolean isJustified, jintArray indents, jintArray leftPaddings, jintArray rightPaddings,
- jint indentsAndPaddingsOffset) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
- b->resize(length);
- env->GetCharArrayRegion(text, 0, length, b->buffer());
- b->setText();
- if (variableTabStops == nullptr) {
- b->setTabStops(nullptr, 0, defaultTabStop);
- } else {
- ScopedIntArrayRO stops(env, variableTabStops);
- b->setTabStops(stops.get(), stops.size(), defaultTabStop);
- }
- b->setStrategy(static_cast<minikin::BreakStrategy>(strategy));
- b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency));
- b->setJustified(isJustified);
-
- // TODO: copy indents and paddings only once when LineBreaker is started to be used.
- b->setLineWidthDelegate(std::make_unique<JNILineBreakerLineWidth>(
- firstWidth, firstWidthLineLimit, restWidth, jintArrayToFloatVector(env, indents),
- jintArrayToFloatVector(env, leftPaddings), jintArrayToFloatVector(env, rightPaddings),
- indentsAndPaddingsOffset));
+static jlong nInit(JNIEnv* env, jclass /* unused */,
+ jint breakStrategy, jint hyphenationFrequency, jboolean isJustified,
+ jintArray indents, jintArray leftPaddings, jintArray rightPaddings) {
+ return reinterpret_cast<jlong>(new StaticLayoutNative(
+ static_cast<minikin::BreakStrategy>(breakStrategy),
+ static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
+ isJustified,
+ jintArrayToFloatVector(env, indents),
+ jintArrayToFloatVector(env, leftPaddings),
+ jintArrayToFloatVector(env, rightPaddings)));
+}
+
+// CriticalNative
+static void nFinish(jlong nativePtr) {
+ delete toNative(nativePtr);
}
static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
@@ -163,42 +263,65 @@ static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
}
static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
- jobject recycle, jintArray recycleBreaks,
- jfloatArray recycleWidths, jfloatArray recycleAscents,
- jfloatArray recycleDescents, jintArray recycleFlags,
- jint recycleLength, jfloatArray charWidths) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
+ // Inputs
+ jcharArray text,
+ jint length,
+ jfloat firstWidth,
+ jint firstWidthLineCount,
+ jfloat restWidth,
+ jintArray variableTabStops,
+ jint defaultTabStop,
+ jint indentsOffset,
+
+ // Outputs
+ jobject recycle,
+ jint recycleLength,
+ jintArray recycleBreaks,
+ jfloatArray recycleWidths,
+ jfloatArray recycleAscents,
+ jfloatArray recycleDescents,
+ jintArray recycleFlags,
+ jfloatArray charWidths) {
+
+ StaticLayoutNative* builder = toNative(nativePtr);
+
+ // TODO: Reorganize minikin APIs.
+ minikin::LineBreaker b;
+ b.resize(length);
+ env->GetCharArrayRegion(text, 0, length, b.buffer());
+ b.setText();
+ if (variableTabStops == nullptr) {
+ b.setTabStops(nullptr, 0, defaultTabStop);
+ } else {
+ ScopedIntArrayRO stops(env, variableTabStops);
+ b.setTabStops(stops.get(), stops.size(), defaultTabStop);
+ }
+ b.setStrategy(builder->getStrategy());
+ b.setHyphenationFrequency(builder->getFrequency());
+ b.setJustified(builder->isJustified());
+ b.setLineWidthDelegate(builder->buildLineWidthDelegate(
+ firstWidth, firstWidthLineCount, restWidth, indentsOffset));
- size_t nBreaks = b->computeBreaks();
+ builder->addRuns(&b);
+
+ size_t nBreaks = b.computeBreaks();
recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
- recycleFlags, recycleLength, nBreaks, b->getBreaks(), b->getWidths(), b->getAscents(),
- b->getDescents(), b->getFlags());
+ recycleFlags, recycleLength, nBreaks, b.getBreaks(), b.getWidths(), b.getAscents(),
+ b.getDescents(), b.getFlags());
- env->SetFloatArrayRegion(charWidths, 0, b->size(), b->charWidths());
+ env->SetFloatArrayRegion(charWidths, 0, b.size(), b.charWidths());
- b->finish();
+ b.finish();
+ builder->clearRuns();
return static_cast<jint>(nBreaks);
}
-static jlong nNewBuilder(JNIEnv*, jclass) {
- return reinterpret_cast<jlong>(new minikin::LineBreaker);
-}
-
-static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) {
- delete reinterpret_cast<minikin::LineBreaker*>(nativePtr);
-}
-
-static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
- b->finish();
-}
-
// Basically similar to Paint.getTextRunAdvances but with C++ interface
-static void nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint, jint start,
- jint end, jboolean isRtl) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
+// CriticalNative
+static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) {
+ StaticLayoutNative* builder = toNative(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
const Typeface* typeface = paint->getAndroidTypeface();
minikin::MinikinPaint minikinPaint;
@@ -206,26 +329,59 @@ static void nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint
minikin::FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, paint,
typeface);
- b->addStyleRun(&minikinPaint, resolvedTypeface->fFontCollection, style, start, end, isRtl);
+ builder->addStyleRun(
+ start, end, std::move(minikinPaint), resolvedTypeface->fFontCollection, std::move(style),
+ isRtl);
}
-static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr, jlong nativePaint,
- jint start, jint end, jfloat width) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
+// CriticalNative
+static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end,
+ jfloat width) {
+ StaticLayoutNative* builder = toNative(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
- b->addReplacement(start, end, width, paint->getMinikinLangListId());
+ builder->addReplacementRun(start, end, width, paint->getMinikinLangListId());
}
static const JNINativeMethod gMethods[] = {
- // TODO performance: many of these are candidates for fast jni, awaiting guidance
- {"nNewBuilder", "()J", (void*) nNewBuilder},
- {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
- {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
- {"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph},
+ // Fast Natives
+ {"nInit", "("
+ "I" // breakStrategy
+ "I" // hyphenationFrequency
+ "Z" // isJustified
+ "[I" // indents
+ "[I" // left paddings
+ "[I" // right paddings
+ ")J", (void*) nInit},
+
+ // Critical Natives
+ {"nFinish", "(J)V", (void*) nFinish},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II[F)I",
- (void*) nComputeLineBreaks}
+
+ // Regular JNI
+ {"nComputeLineBreaks", "("
+ "J" // nativePtr
+
+ // Inputs
+ "[C" // text
+ "I" // length
+ "F" // firstWidth
+ "I" // firstWidthLineCount
+ "F" // restWidth
+ "[I" // variableTabStops
+ "I" // defaultTabStop
+ "I" // indentsOffset
+
+ // Outputs
+ "Landroid/text/StaticLayout$LineBreaks;" // recycle
+ "I" // recycleLength
+ "[I" // recycleBreaks
+ "[F" // recycleWidths
+ "[F" // recycleAscents
+ "[F" // recycleDescents
+ "[I" // recycleFlags
+ "[F" // charWidths
+ ")I", (void*) nComputeLineBreaks}
};
int register_android_text_StaticLayout(JNIEnv* env)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 7908c9d2b609..6362bc7187fc 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -32,6 +32,7 @@
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
+#include <binder/BpBinder.h>
#include <binder/ProcessState.h>
#include <log/log.h>
#include <utils/Atomic.h>
@@ -80,9 +81,17 @@ static struct binderinternal_offsets_t
// Class state.
jclass mClass;
jmethodID mForceGc;
+ jmethodID mProxyLimitCallback;
} gBinderInternalOffsets;
+static struct sparseintarray_offsets_t
+{
+ jclass classObject;
+ jmethodID constructor;
+ jmethodID put;
+} gSparseIntArrayOffsets;
+
// ----------------------------------------------------------------------------
static struct error_offsets_t
@@ -973,6 +982,43 @@ static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz)
android_atomic_and(0, &gNumRefsCreated);
}
+static void android_os_BinderInternal_proxyLimitcallback(int uid)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
+ gBinderInternalOffsets.mProxyLimitCallback,
+ uid);
+}
+
+static void android_os_BinderInternal_setBinderProxyCountEnabled(JNIEnv* env, jobject clazz,
+ jboolean enable)
+{
+ BpBinder::setCountByUidEnabled((bool) enable);
+}
+
+static jobject android_os_BinderInternal_getBinderProxyPerUidCounts(JNIEnv* env, jclass clazz)
+{
+ Vector<uint32_t> uids, counts;
+ BpBinder::getCountByUid(uids, counts);
+ jobject sparseIntArray = env->NewObject(gSparseIntArrayOffsets.classObject,
+ gSparseIntArrayOffsets.constructor);
+ for (size_t i = 0; i < uids.size(); i++) {
+ env->CallVoidMethod(sparseIntArray, gSparseIntArrayOffsets.put,
+ static_cast<jint>(uids[i]), static_cast<jint>(counts[i]));
+ }
+ return sparseIntArray;
+}
+
+static jint android_os_BinderInternal_getBinderProxyCount(JNIEnv* env, jobject clazz, jint uid) {
+ return static_cast<jint>(BpBinder::getBinderProxyCount(static_cast<uint32_t>(uid)));
+}
+
+static void android_os_BinderInternal_setBinderProxyCountWatermarks(JNIEnv* env, jobject clazz,
+ jint high, jint low)
+{
+ BpBinder::setBinderProxyCountWatermarks(high, low);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gBinderInternalMethods[] = {
@@ -981,7 +1027,11 @@ static const JNINativeMethod gBinderInternalMethods[] = {
{ "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },
{ "disableBackgroundScheduling", "(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },
{ "setMaxThreads", "(I)V", (void*)android_os_BinderInternal_setMaxThreads },
- { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc }
+ { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc },
+ { "nSetBinderProxyCountEnabled", "(Z)V", (void*)android_os_BinderInternal_setBinderProxyCountEnabled },
+ { "nGetBinderProxyPerUidCounts", "()Landroid/util/SparseIntArray;", (void*)android_os_BinderInternal_getBinderProxyPerUidCounts },
+ { "nGetBinderProxyCount", "(I)I", (void*)android_os_BinderInternal_getBinderProxyCount },
+ { "nSetBinderProxyCountWatermarks", "(II)V", (void*)android_os_BinderInternal_setBinderProxyCountWatermarks}
};
const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";
@@ -992,6 +1042,16 @@ static int int_register_android_os_BinderInternal(JNIEnv* env)
gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");
+ gBinderInternalOffsets.mProxyLimitCallback = GetStaticMethodIDOrDie(env, clazz, "binderProxyLimitCallbackFromNative", "(I)V");
+
+ jclass SparseIntArrayClass = FindClassOrDie(env, "android/util/SparseIntArray");
+ gSparseIntArrayOffsets.classObject = MakeGlobalRefOrDie(env, SparseIntArrayClass);
+ gSparseIntArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject,
+ "<init>", "()V");
+ gSparseIntArrayOffsets.put = GetMethodIDOrDie(env, gSparseIntArrayOffsets.classObject, "put",
+ "(II)V");
+
+ BpBinder::setLimitCallback(android_os_BinderInternal_proxyLimitcallback);
return RegisterMethodsOrDie(
env, kBinderInternalPathName,
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
new file mode 100644
index 000000000000..e87499ed2b83
--- /dev/null
+++ b/core/proto/android/app/activitymanager.proto
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.app;
+
+option java_multiple_files = true;
+
+message ActivityManagerProto {
+
+ // ActivityManager.java PROCESS_STATEs
+ enum ProcessState {
+ // Order matters for process states, so values have been spaced to provide
+ // room for future additions.
+
+ // Not a real process state.
+ PROCESS_STATE_UNKNOWN = -100;
+ // Process is a persistent system process.
+ PROCESS_STATE_PERSISTENT = 0;
+ // Process is a persistent system process and is doing UI.
+ PROCESS_STATE_PERSISTENT_UI = 100;
+ // Process is hosting the current top activities. Note that this covers
+ // all activities that are visible to the user.
+ PROCESS_STATE_TOP = 200;
+ // Process is hosting a foreground service due to a system binding.
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300;
+ // Process is hosting a foreground service.
+ PROCESS_STATE_FOREGROUND_SERVICE = 400;
+ // Same as PROCESS_STATE_TOP but while device is sleeping.
+ PROCESS_STATE_TOP_SLEEPING = 500;
+ // Process is important to the user, and something they are aware of.
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 600;
+ // Process is important to the user, but not something they are aware of.
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 700;
+ // Process is in the background transient so we will try to keep running.
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 800;
+ // Process is in the background running a backup/restore operation.
+ PROCESS_STATE_BACKUP = 900;
+ // Process is in the background, but it can't restore its state so we want
+ // to try to avoid killing it.
+ PROCESS_STATE_HEAVY_WEIGHT = 1000;
+ // Process is in the background running a service. Unlike oom_adj, this
+ // level is used for both the normal running in background state and the
+ // executing operations state.
+ PROCESS_STATE_SERVICE = 1100;
+ // Process is in the background running a receiver. Note that from the
+ // perspective of oom_adj, receivers run at a higher foreground level, but
+ // for our prioritization here that is not necessary and putting them
+ // below services means many fewer changes in some process states as they
+ // receive broadcasts.
+ PROCESS_STATE_RECEIVER = 1200;
+ // Process is in the background but hosts the home activity.
+ PROCESS_STATE_HOME = 1300;
+ // Process is in the background but hosts the last shown activity.
+ PROCESS_STATE_LAST_ACTIVITY = 1400;
+ // Process is being cached for later use and contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY = 1500;
+ // Process is being cached for later use and is a client of another cached
+ // process that contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600;
+ // Process is being cached for later use and is empty.
+ PROCESS_STATE_CACHED_EMPTY = 1700;
+ // Process does not exist.
+ PROCESS_STATE_NONEXISTENT = 1800;
+ }
+}
diff --git a/core/proto/android/app/alarmmanager.proto b/core/proto/android/app/alarmmanager.proto
new file mode 100644
index 000000000000..789e3d6293c3
--- /dev/null
+++ b/core/proto/android/app/alarmmanager.proto
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/pendingintent.proto";
+
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.AlarmManager object.
+ */
+message AlarmManagerProto {
+ enum AlarmType {
+ // Alarm time in System.currentTimeMillis() (wall clock time in UTC), which
+ // will wake up the device when it goes off.
+ RTC_WAKEUP = 0;
+ // Alarm time in System.currentTimeMillis() (wall clock time in UTC). This
+ // alarm does not wake the device up; if it goes off while the device is
+ // asleep, it will not be delivered until the next time the device wakes up.
+ RTC = 1;
+ // Alarm time in SystemClock.elapsedRealtime() (time since boot, including
+ // sleep), which will wake up the device when it goes off.
+ ELAPSED_REALTIME_WAKEUP = 2;
+ // Alarm time in SystemClock.elapsedRealtime() (time since boot, including
+ // sleep). This alarm does not wake the device up; if it goes off while the
+ // device is asleep, it will not be delivered until the next time the device
+ // wakes up.
+ ELAPSED_REALTIME = 3;
+ }
+}
+
+// An android.app.AlarmManager.AlarmClockInfo object.
+message AlarmClockInfoProto {
+ // This value is UTC wall clock time in milliseconds, as returned by
+ // System#currentTimeMillis() for example.
+ optional int64 trigger_time_ms = 1;
+ optional android.app.PendingIntentProto show_intent = 2;
+}
diff --git a/core/proto/android/app/pendingintent.proto b/core/proto/android/app/pendingintent.proto
new file mode 100644
index 000000000000..b562c0bb38e5
--- /dev/null
+++ b/core/proto/android/app/pendingintent.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.PendingIntent object.
+ */
+message PendingIntentProto {
+ optional string target = 1;
+}
diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto
index 4f49744d9c5a..3e5265ad9c13 100644
--- a/core/proto/android/content/intent.proto
+++ b/core/proto/android/content/intent.proto
@@ -15,15 +15,37 @@
*/
syntax = "proto2";
+package android.content;
+
option java_package = "android.content";
option java_multiple_files = true;
import "frameworks/base/core/proto/android/os/patternmatcher.proto";
-package android.content;
-
// Next Tag: 13
message IntentProto {
+ enum DockState {
+ // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+ // the phone is not in any dock.
+ DOCK_STATE_UNDOCKED = 0;
+
+ // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+ // the phone is in a desk dock.
+ DOCK_STATE_DESK = 1;
+
+ // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+ // the phone is in a car dock.
+ DOCK_STATE_CAR = 2;
+
+ // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+ // the phone is in a analog (low end) dock.
+ DOCK_STATE_LE_DESK = 3;
+
+ // Used as an int value for Intent#EXTRA_DOCK_STATE to represent that
+ // the phone is in a digital (high end) dock.
+ DOCK_STATE_HE_DESK = 4;
+ }
+
optional string action = 1;
repeated string categories = 2;
optional string data = 3;
diff --git a/core/proto/android/internal/locallog.proto b/core/proto/android/internal/locallog.proto
new file mode 100644
index 000000000000..51f6c1ce2fd7
--- /dev/null
+++ b/core/proto/android/internal/locallog.proto
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.internal.util;
+
+option java_multiple_files = true;
+
+message LocalLogProto {
+ repeated string lines = 1;
+}
diff --git a/core/proto/android/os/batterymanager.proto b/core/proto/android/os/batterymanager.proto
new file mode 100644
index 000000000000..669bf2d0a6e7
--- /dev/null
+++ b/core/proto/android/os/batterymanager.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.os;
+
+option java_multiple_files = true;
+
+message BatteryManagerProto {
+ enum PlugType {
+ PLUG_TYPE_NONE = 0;
+ PLUG_TYPE_AC = 1;
+ PLUG_TYPE_USB = 2;
+ PLUG_TYPE_WIRELESS = 4;
+ }
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 5a5454e2f6ae..f716ffee53af 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,6 +20,7 @@ option java_outer_classname = "IncidentProtoMetadata";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
+import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
import "frameworks/base/core/proto/android/service/battery.proto";
import "frameworks/base/core/proto/android/service/batterystats.proto";
@@ -28,10 +29,10 @@ import "frameworks/base/core/proto/android/service/diskstats.proto";
import "frameworks/base/core/proto/android/service/netstats.proto";
import "frameworks/base/core/proto/android/service/notification.proto";
import "frameworks/base/core/proto/android/service/package.proto";
-import "frameworks/base/core/proto/android/service/power.proto";
import "frameworks/base/core/proto/android/service/print.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
+import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -105,7 +106,11 @@ message IncidentProto {
(section).args = "package --proto"
];
- optional android.service.power.PowerServiceDumpProto power = 3009;
+ optional com.android.server.power.PowerManagerServiceDumpProto power = 3009 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "power --proto"
+ ];
+
optional android.service.print.PrintServiceDumpProto print = 3010;
optional android.service.procstats.ProcessStatsServiceDumpProto procstats = 3011 [
@@ -125,4 +130,9 @@ message IncidentProto {
optional com.android.server.am.proto.ServiceProto amservices = 3014;
optional com.android.server.am.proto.ProcessProto amprocesses = 3015;
+
+ optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "alarm --proto"
+ ];
}
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
index 3bfe5d68eaae..e9f409d0d75d 100644
--- a/core/proto/android/os/powermanager.proto
+++ b/core/proto/android/os/powermanager.proto
@@ -15,10 +15,10 @@
*/
syntax = "proto2";
-option java_multiple_files = true;
-
package android.os;
+option java_multiple_files = true;
+
message PowerManagerProto {
/* User activity events in PowerManager.java. */
enum UserActivityEvent {
@@ -31,4 +31,84 @@ message PowerManagerProto {
// Accessibility taking action on behalf of user.
USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;
}
+
+ enum WakeLockLevel {
+ // NOTE: Wake lock levels were previously defined as a bit field, except
+ // that only a few combinations were actually supported so the bit field
+ // was removed. This explains why the numbering scheme is so odd. If
+ // adding a new wake lock level, any unused value can be used.
+
+ // Ensures that the CPU is running; the screen and keyboard backlight
+ // will be allowed to go off.
+ PARTIAL_WAKE_LOCK = 1;
+
+ // Ensures that the screen is on (but may be dimmed); the keyboard
+ // backlight will be allowed to go off. If the user presses the power
+ // button, then the SCREEN_DIM_WAKE_LOCK will be implicitly released by
+ // the system, causing both the screen and the CPU to be turned off.
+ SCREEN_DIM_WAKE_LOCK = 6 [deprecated = true];
+
+ // Ensures that the screen is on at full brightness; the keyboard
+ // backlight will be allowed to go off. If the user presses the power
+ // button, then the SCREEN_BRIGHT_WAKE_LOCK will be implicitly released
+ // by the system, causing both the screen and the CPU to be turned off.
+ SCREEN_BRIGHT_WAKE_LOCK = 10 [deprecated = true];
+
+ // Ensures that the screen and keyboard backlight are on at full
+ // brightness. If the user presses the power button, then the
+ // FULL_WAKE_LOCK will be implicitly released by the system, causing
+ // both the screen and the CPU to be turned off.
+ FULL_WAKE_LOCK = 26 [deprecated = true];
+
+ // Turns the screen off when the proximity sensor activates. If the
+ // proximity sensor detects that an object is nearby, the screen turns
+ // off immediately. Shortly after the object moves away, the screen
+ // turns on again.
+ // A proximity wake lock does not prevent the device from falling asleep
+ // unlike FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and
+ // SCREEN_DIM_WAKE_LOCK. If there is no user activity and no other wake
+ // locks are held, then the device will fall asleep (and lock) as usual.
+ // However, the device will not fall asleep while the screen has been
+ // turned off by the proximity sensor because it effectively counts as
+ // ongoing user activity.
+ PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32;
+
+ // Put the screen in a low power state and allow the CPU to suspend if
+ // no other wake locks are held. This is used by the dream manager to
+ // implement doze mode. It currently has no effect unless the power
+ // manager is in the dozing state.
+ DOZE_WAKE_LOCK = 64;
+
+ // Keep the device awake enough to allow drawing to occur. This is used
+ // by the window manager to allow applications to draw while the system
+ // is dozing. It currently has no effect unless the power manager is in
+ // the dozing state.
+ DRAW_WAKE_LOCK = 128;
+ }
+}
+
+message PowerManagerInternalProto {
+ // Enum values gotten from PowerManagerInternal.java
+ enum Wakefulness {
+ // The device is asleep. It can only be awoken by a call to wakeUp().
+ // The screen should be off or in the process of being turned off by the
+ // display controller. The device typically passes through the dozing
+ // state first.
+ WAKEFULNESS_ASLEEP = 0;
+ // The device is fully awake. It can be put to sleep by a call to
+ // goToSleep(). When the user activity timeout expires, the device may
+ // start dreaming or go to sleep.
+ WAKEFULNESS_AWAKE = 1;
+ // The device is dreaming. It can be awoken by a call to wakeUp(), which
+ // ends the dream. The device goes to sleep when goToSleep() is called,
+ // when the dream ends, or when unplugged. User activity may brighten
+ // the screen but does not end the dream.
+ WAKEFULNESS_DREAMING = 2;
+ // The device is dozing. It is almost asleep but is allowing a special
+ // low-power "doze" dream to run which keeps the display on but lets the
+ // application processor suspend. It can be awoken by a call to wakeUp()
+ // which ends the dream. The device fully goes to sleep if the dream
+ // cannot be started or ends on its own.
+ WAKEFULNESS_DOZING = 3;
+ }
}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index f0927134c37c..3411c6a6f1d4 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -599,6 +599,14 @@ message SettingProto {
optional bool default_from_system = 6;
}
+message SettingsProto {
+ // Enum values gotten from Settings.java
+ enum ScreenBrightnessMode {
+ SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
+ SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
+ }
+}
+
message SettingsOperationProto {
// When the operation happened
optional int64 timestamp = 1;
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 788ac8f06840..c57cb72c8b68 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -69,12 +69,11 @@ message TaskRecordProto {
optional string real_activity = 6;
optional string orig_activity = 7;
optional int32 activity_type = 8;
- optional int32 return_to_type = 9;
- optional int32 resize_mode = 10;
- optional bool fullscreen = 11;
- optional .android.graphics.RectProto bounds = 12;
- optional int32 min_width = 13;
- optional int32 min_height = 14;
+ optional int32 resize_mode = 9;
+ optional bool fullscreen = 10;
+ optional .android.graphics.RectProto bounds = 11;
+ optional int32 min_width = 12;
+ optional int32 min_height = 13;
}
message ActivityRecordProto {
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
new file mode 100644
index 000000000000..d2cd19072128
--- /dev/null
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/alarmmanager.proto";
+import "frameworks/base/core/proto/android/app/pendingintent.proto";
+import "frameworks/base/core/proto/android/internal/locallog.proto";
+import "frameworks/base/core/proto/android/os/worksource.proto";
+
+package com.android.server;
+
+option java_multiple_files = true;
+
+message AlarmManagerServiceProto {
+ optional int64 current_time = 1;
+ optional int64 elapsed_realtime = 2;
+ optional int64 last_time_change_clock_time = 3;
+ optional int64 last_time_change_realtime = 4;
+ // Current settings
+ optional ConstantsProto settings = 5;
+ // UIDs currently in the foreground.
+ repeated int32 foreground_uids = 6;
+ // Packages forced into app standby.
+ repeated string forced_app_standby_packages = 7;
+
+ optional bool is_interactive = 8;
+ // Only valid if is_interactive is false.
+ optional int64 time_since_non_interactive_ms = 9;
+ // Only valid if is_interactive is false.
+ optional int64 max_wakeup_delay_ms = 10;
+ // Only valid if is_interactive is false.
+ optional int64 time_since_last_dispatch_ms = 11;
+ // Only valid if is_interactive is false.
+ optional int64 time_until_next_non_wakeup_delivery_ms = 12;
+
+ optional int64 time_until_next_non_wakeup_alarm_ms = 13;
+ optional int64 time_until_next_wakeup_ms = 14;
+ optional int64 time_since_last_wakeup_ms = 15;
+ // Time since the last wakeup was set.
+ optional int64 time_since_last_wakeup_set_ms = 16;
+ optional int64 time_change_event_count = 17;
+ // The current set of user whitelisted apps for device idle mode, meaning
+ // these are allowed to freely schedule alarms. These are app IDs, not UIDs.
+ repeated int32 device_idle_user_whitelist_app_ids = 18;
+
+ repeated AlarmClockMetadataProto next_alarm_clock_metadata = 19;
+
+ repeated BatchProto pending_alarm_batches = 20;
+
+ // List of alarms per uid deferred due to user applied background restrictions
+ // on the source app.
+ repeated AlarmProto pending_user_blocked_background_alarms = 21;
+
+ // When idling mode will end. Will be empty if the device is not currently
+ // idling.
+ optional AlarmProto pending_idle_until = 22;
+
+ // Any alarms that we don't want to run during idle mode. Will be empty if the
+ // device is not currently idling.
+ repeated AlarmProto pending_while_idle_alarms = 23;
+
+ // This is a special alarm that will put the system into idle until it goes
+ // off. The caller has given the time they want this to happen at.
+ optional AlarmProto next_wake_from_idle = 24;
+
+ repeated AlarmProto past_due_non_wakeup_alarms = 25;
+
+ // Number of delayed alarms.
+ optional int32 delayed_alarm_count = 26;
+ // The total amount of time alarms had been delayed. Overlapping alarms are
+ // only counted once (ie. If two alarms were meant to trigger at the same time
+ // but were delayed by 5 seconds, the total time would be 5 seconds).
+ optional int64 total_delay_time_ms = 27;
+ optional int64 max_delay_duration_ms = 28;
+ optional int64 max_non_interactive_duration_ms = 29;
+
+ optional int32 broadcast_ref_count = 30;
+ // Canonical count of (operation.send() - onSendFinished()) and listener
+ // send/complete/timeout invocations.
+ optional int32 pending_intent_send_count = 31;
+ optional int32 pending_intent_finish_count = 32;
+ optional int32 listener_send_count = 33;
+ optional int32 listener_finish_count = 34;
+
+ repeated InFlightProto outstanding_deliveries = 35;
+
+ // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. It
+ // should be either CosntantsProto.allow_while_idle_short_duration_ms or
+ // ConstantsProto.allow_while_idle_long_duration_ms.
+ optional int64 allow_while_idle_min_duration_ms = 36;
+
+ message LastAllowWhileIdleDispatch {
+ optional int32 uid = 1;
+ // In the 'elapsed' timebase.
+ optional int64 time_ms = 2;
+ }
+ // For each uid, this is the last time we dispatched an "allow while idle"
+ // alarm, used to determine the earliest we can dispatch the next such alarm.
+ repeated LastAllowWhileIdleDispatch last_allow_while_idle_dispatch_times = 37;
+
+ optional com.android.internal.util.LocalLogProto recent_problems = 38;
+
+ message TopAlarm {
+ optional int32 uid = 1;
+ optional string package_name = 2;
+ optional FilterStatsProto filter = 3;
+ }
+ repeated TopAlarm top_alarms = 39;
+
+ message AlarmStat {
+ optional BroadcastStatsProto broadcast = 1;
+ repeated FilterStatsProto filters = 2;
+ }
+ repeated AlarmStat alarm_stats = 40;
+
+ repeated IdleDispatchEntryProto allow_while_idle_dispatches = 41;
+ repeated WakeupEventProto recent_wakeup_history = 42;
+}
+
+// This is a soft wrapper for alarm clock information. It is not representative
+// of an android.app.AlarmManager.AlarmClockInfo object.
+message AlarmClockMetadataProto {
+ optional int32 user = 1;
+ optional bool is_pending_send = 2;
+ // This value is UTC wall clock time in milliseconds, as returned by
+ // System#currentTimeMillis() for example.
+ optional int64 trigger_time_ms = 3;
+}
+
+// A com.android.server.AlarmManagerService.Alarm object.
+message AlarmProto {
+ optional string tag = 1;
+ optional .android.app.AlarmManagerProto.AlarmType type = 2;
+ // How long until the alarm goes off, in the 'elapsed' timebase.
+ optional int64 when_elapsed_ms = 3;
+ optional int64 window_length_ms = 4;
+ optional int64 repeat_interval_ms = 5;
+ optional int32 count = 6;
+ optional int32 flags = 7;
+ optional .android.app.AlarmClockInfoProto alarm_clock = 8;
+ optional .android.app.PendingIntentProto operation = 9;
+ optional string listener = 10;
+}
+
+// A com.android.server.AlarmManagerService.Batch object.
+message BatchProto {
+ // Start time in terms of elapsed realtime.
+ optional int64 start_realtime = 1;
+ // End time in terms of elapsed realtime.
+ optional int64 end_realtime = 2;
+ optional int32 flags = 3;
+ repeated AlarmProto alarms = 4;
+}
+
+// A com.android.server.AlarmManagerService.BroadcastStats object.
+message BroadcastStatsProto {
+ optional int32 uid = 1;
+ optional string package_name = 2;
+ // The total amount of time this broadcast was in flight.
+ optional int64 total_flight_duration_ms = 3;
+ optional int32 count = 4;
+ optional int32 wakeup_count = 5;
+ // The last time this first became active (when nesting changed from 0 to 1)
+ // in terms of elapsed realtime.
+ optional int64 start_time_realtime = 6;
+ // The broadcast is active if nesting > 0.
+ optional int32 nesting = 7;
+}
+
+// A com.android.server.AlarmManagerService.Constants object.
+message ConstantsProto {
+ // Minimum futurity of a new alarm.
+ optional int64 min_futurity_duration_ms = 1;
+ // Minimum alarm recurrence interval.
+ optional int64 min_interval_duration_ms = 2;
+ // Direct alarm listener callback timeout.
+ optional int64 listener_timeout_duration_ms = 3;
+ // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
+ optional int64 allow_while_idle_short_duration_ms = 4;
+ // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling.
+ optional int64 allow_while_idle_long_duration_ms = 5;
+ // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
+ optional int64 allow_while_idle_whitelist_duration_ms = 6;
+}
+
+// A com.android.server.AlarmManagerService.FilterStats object.
+message FilterStatsProto {
+ optional string tag = 1;
+ // The last time this filter when in flight, in terms of elapsed realtime.
+ optional int64 last_flight_time_realtime = 2;
+ // The total amount of time this filter was in flight.
+ optional int64 total_flight_duration_ms = 3;
+ optional int32 count = 4;
+ optional int32 wakeup_count = 5;
+ // The last time this first became active (when nesting changed from 0 to 1)
+ // in terms of elapsed realtime.
+ optional int64 start_time_realtime = 6;
+ // The filter is active if nesting > 0.
+ optional int32 nesting = 7;
+}
+
+// A com.android.server.AlarmManagerService.IdleDispatchEntry object.
+message IdleDispatchEntryProto {
+ optional int32 uid = 1;
+ optional string pkg = 2;
+ optional string tag = 3;
+ optional string op = 4;
+ // Time when this entry was created, in terms of elapsed realtime.
+ optional int64 entry_creation_realtime = 5;
+ // For a RESCHEDULED op, this is the last time we dispatched an "allow while
+ // idle" alarm for the UID. For a SET op, this is when the alarm was
+ // triggered. Times are in the 'elapsed' timebase.
+ optional int64 arg_realtime = 6;
+}
+
+// A com.android.server.AlarmManagerService.InFlight object.
+message InFlightProto {
+ optional int32 uid = 1;
+ optional string tag = 2;
+ optional int64 when_elapsed_ms = 3;
+ optional .android.app.AlarmManagerProto.AlarmType alarm_type = 4;
+ optional .android.app.PendingIntentProto pending_intent = 5;
+ optional BroadcastStatsProto broadcast_stats = 6;
+ optional FilterStatsProto filter_stats = 7;
+ optional .android.os.WorkSourceProto work_source = 8;
+}
+
+// A com.android.server.AlarmManagerService.WakeupEvent object.
+message WakeupEventProto {
+ optional int32 uid = 1;
+ optional string action = 2;
+ optional int64 when = 3;
+}
diff --git a/core/proto/android/service/power.proto b/core/proto/android/server/powermanagerservice.proto
index 5d53847a65aa..d442acfd91be 100644
--- a/core/proto/android/service/power.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -15,16 +15,22 @@
*/
syntax = "proto2";
-package android.service.power;
+package com.android.server.power;
option java_multiple_files = true;
-option java_outer_classname = "PowerServiceProto";
+import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/content/intent.proto";
+import "frameworks/base/core/proto/android/os/batterymanager.proto";
import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/os/powermanager.proto";
import "frameworks/base/core/proto/android/os/worksource.proto";
-import "frameworks/base/core/proto/android/service/wirelesschargerdetector.proto";
+import "frameworks/base/core/proto/android/providers/settings.proto";
+import "frameworks/base/core/proto/android/server/wirelesschargerdetector.proto";
+import "frameworks/base/core/proto/android/view/display.proto";
-message PowerServiceDumpProto {
+message PowerManagerServiceDumpProto {
+ // A com.android.server.power.PowerManagerService.Constants object.
message ConstantsProto {
optional bool is_no_cached_wake_locks = 1;
}
@@ -44,79 +50,14 @@ message PowerServiceDumpProto {
optional bool is_screen_dim = 2;
optional bool is_screen_dream = 3;
}
- message UidProto {
- // Enum values gotten from ActivityManager.java
- enum ProcessState {
- // Process is a persistent system process.
- PROCESS_STATE_PERSISTENT = 0;
- // Process is a persistent system process and is doing UI.
- PROCESS_STATE_PERSISTENT_UI = 1;
- // Process is hosting the current top activities. Note that this
- // covers all activities that are visible to the user.
- PROCESS_STATE_TOP = 2;
- // Process is hosting a foreground service due to a system binding.
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;
- // Process is hosting a foreground service.
- PROCESS_STATE_FOREGROUND_SERVICE = 4;
- // Same as {@link #PROCESS_STATE_TOP} but while device is sleeping.
- PROCESS_STATE_TOP_SLEEPING = 5;
- // Process is important to the user, and something they are aware of.
- PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
- // Process is important to the user, but not something they are aware of.
- PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
- // Process is in the background running a backup/restore operation.
- PROCESS_STATE_BACKUP = 8;
- // Process is in the background, but it can't restore its state so
- // we want to try to avoid killing it.
- PROCESS_STATE_HEAVY_WEIGHT = 9;
- // Process is in the background running a service.
- PROCESS_STATE_SERVICE = 10;
- // Process is in the background running a receiver.
- PROCESS_STATE_RECEIVER = 11;
- // Process is in the background but hosts the home activity.
- PROCESS_STATE_HOME = 12;
- // Process is in the background but hosts the last shown activity.
- PROCESS_STATE_LAST_ACTIVITY = 13;
- // Process is being cached for later use and contains activities.
- PROCESS_STATE_CACHED_ACTIVITY = 14;
- // Process is being cached for later use and is a client of another
- // cached process that contains activities.
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;
- // Process is being cached for later use and is empty.
- PROCESS_STATE_CACHED_EMPTY = 16;
- // Process does not exist.
- PROCESS_STATE_NONEXISTENT = 17;
- }
+ // A com.android.server.power.PowerManagerService.UidState object.
+ message UidStateProto {
optional int32 uid = 1;
optional string uid_string = 2;
optional bool is_active = 3;
optional int32 num_wake_locks = 4;
optional bool is_process_state_unknown = 5;
- optional ProcessState process_state = 6;
- }
-
- // Enum values gotten from PowerManagerInternal.java
- enum Wakefulness {
- WAKEFULNESS_ASLEEP = 0;
- WAKEFULNESS_AWAKE = 1;
- WAKEFULNESS_DREAMING = 2;
- WAKEFULNESS_DOZING = 3;
- WAKEFULNESS_UNKNOWN = 4;
- }
- // Enum values gotten from BatteryManager.java
- enum PlugType {
- PLUG_TYPE_NONE = 0;
- PLUG_TYPE_PLUGGED_AC = 1;
- PLUG_TYPE_PLUGGED_USB = 2;
- PLUG_TYPE_PLUGGED_WIRELESS = 4;
- }
- // Enum values gotten from Intent.java
- enum DockState {
- DOCK_STATE_UNDOCKED = 0;
- DOCK_STATE_DESK = 1;
- DOCK_STATE_CAR = 2;
- DOCK_STATE_LE_DESK = 3;
- DOCK_STATE_HE_DESK = 4;
+ optional .android.app.ActivityManagerProto.ProcessState process_state = 6;
}
optional ConstantsProto constants = 1;
@@ -124,18 +65,18 @@ message PowerServiceDumpProto {
// changed and need to be recalculated.
optional int32 dirty = 2;
// Indicates whether the device is awake or asleep or somewhere in between.
- optional Wakefulness wakefulness = 3;
+ optional .android.os.PowerManagerInternalProto.Wakefulness wakefulness = 3;
optional bool is_wakefulness_changing = 4;
// True if the device is plugged into a power source.
optional bool is_powered = 5;
// The current plug type
- optional PlugType plug_type = 6;
+ optional .android.os.BatteryManagerProto.PlugType plug_type = 6;
// The current battery level percentage.
optional int32 battery_level = 7;
// The battery level percentage at the time the dream started.
optional int32 battery_level_when_dream_started = 8;
// The current dock state.
- optional DockState dock_state = 9;
+ optional .android.content.IntentProto.DockState dock_state = 9;
// True if the device should stay on.
optional bool is_stay_on = 10;
// True if the proximity sensor reads a positive result.
@@ -215,8 +156,8 @@ message PowerServiceDumpProto {
// Some uids have actually changed while mUidsChanging was true.
optional bool are_uids_changed = 45;
// List of UIDs and their states
- repeated UidProto uids = 46;
- optional android.os.LooperProto looper = 47;
+ repeated UidStateProto uid_states = 46;
+ optional .android.os.LooperProto looper = 47;
// List of all wake locks acquired by applications.
repeated WakeLockProto wake_locks = 48;
// List of all suspend blockers.
@@ -224,11 +165,13 @@ message PowerServiceDumpProto {
optional WirelessChargerDetectorProto wireless_charger_detector = 50;
}
+// A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
message SuspendBlockerProto {
optional string name = 1;
optional int32 reference_count = 2;
}
+// A com.android.server.power.PowerManagerService.WakeLock object.
message WakeLockProto {
message WakeLockFlagsProto {
// Turn the screen on when the wake lock is acquired.
@@ -238,27 +181,7 @@ message WakeLockProto {
optional bool is_on_after_release = 2;
}
- // Enum values gotten from PowerManager.java
- enum LockLevel {
- WAKE_LOCK_INVALID = 0;
- // Ensures that the CPU is running.
- PARTIAL_WAKE_LOCK = 1;
- // Ensures that the screen is on (but may be dimmed).
- SCREEN_DIM_WAKE_LOCK = 6;
- // Ensures that the screen is on at full brightness.
- SCREEN_BRIGHT_WAKE_LOCK = 10;
- // Ensures that the screen and keyboard backlight are on at full brightness.
- FULL_WAKE_LOCK = 26;
- // Turns the screen off when the proximity sensor activates.
- PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32;
- // Put the screen in a low power state and allow the CPU to suspend
- // if no other wake locks are held.
- DOZE_WAKE_LOCK = 64;
- // Keep the device awake enough to allow drawing to occur.
- DRAW_WAKE_LOCK = 128;
- }
-
- optional LockLevel lock_level = 1;
+ optional .android.os.PowerManagerProto.WakeLockLevel lock_level = 1;
optional string tag = 2;
optional WakeLockFlagsProto flags = 3;
optional bool is_disabled = 4;
@@ -269,7 +192,7 @@ message WakeLockProto {
optional int32 uid = 7;
// Owner PID
optional int32 pid = 8;
- optional android.os.WorkSourceProto work_source = 9;
+ optional .android.os.WorkSourceProto work_source = 9;
}
message PowerServiceSettingsAndConfigurationDumpProto {
@@ -285,22 +208,6 @@ message PowerServiceSettingsAndConfigurationDumpProto {
optional int32 setting_for_vr_default = 4;
}
- // Enum values gotten from Settings.java
- enum ScreenBrightnessMode {
- SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
- SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
- }
- // Enum values gotten from Display.java
- enum DisplayState {
- DISPLAY_STATE_UNKNOWN = 0;
- DISPLAY_STATE_OFF = 1;
- DISPLAY_STATE_ON = 2;
- DISPLAY_STATE_DOZE = 3;
- DISPLAY_STATE_DOZE_SUSPEND = 4;
- DISPLAY_STATE_VR = 5;
- }
-
-
// True to decouple auto-suspend mode from the display state.
optional bool is_decouple_hal_auto_suspend_mode_from_display_config = 1;
// True to decouple interactive mode from the display state.
@@ -371,7 +278,7 @@ message PowerServiceSettingsAndConfigurationDumpProto {
// Use 0 if there is no adjustment.
optional float screen_auto_brightness_adjustment_setting = 31;
// The screen brightness mode.
- optional ScreenBrightnessMode screen_brightness_mode_setting = 32;
+ optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 32;
// The screen brightness setting override from the window manager
// to allow the current foreground activity to override the brightness.
// Use -1 to disable.
@@ -393,7 +300,7 @@ message PowerServiceSettingsAndConfigurationDumpProto {
// Use NaN to disable.
optional float temporary_screen_auto_brightness_adjustment_setting_override = 37;
// The screen state to use while dozing.
- optional DisplayState doze_screen_state_override_from_dream_manager = 38;
+ optional .android.view.DisplayProto.DisplayState doze_screen_state_override_from_dream_manager = 38;
// The screen brightness to use while dozing.
optional float dozed_screen_brightness_override_from_dream_manager = 39;
// Screen brightness settings limits.
diff --git a/core/proto/android/service/wirelesschargerdetector.proto b/core/proto/android/server/wirelesschargerdetector.proto
index bd697c85b8ae..89cf2f8900fe 100644
--- a/core/proto/android/service/wirelesschargerdetector.proto
+++ b/core/proto/android/server/wirelesschargerdetector.proto
@@ -15,7 +15,7 @@
*/
syntax = "proto2";
-package android.service.power;
+package com.android.server.power;
option java_multiple_files = true;
@@ -46,4 +46,4 @@ message WirelessChargerDetectorProto {
optional VectorProto first_sample = 9;
// The value of the last sample that was collected.
optional VectorProto last_sample = 10;
-} \ No newline at end of file
+}
diff --git a/core/proto/android/service/battery.proto b/core/proto/android/service/battery.proto
index 998a808b6e52..8382b8262b95 100644
--- a/core/proto/android/service/battery.proto
+++ b/core/proto/android/service/battery.proto
@@ -20,13 +20,9 @@ package android.service.battery;
option java_multiple_files = true;
option java_outer_classname = "BatteryServiceProto";
+import "frameworks/base/core/proto/android/os/batterymanager.proto";
+
message BatteryServiceDumpProto {
- enum BatteryPlugged {
- BATTERY_PLUGGED_NONE = 0;
- BATTERY_PLUGGED_AC = 1;
- BATTERY_PLUGGED_USB = 2;
- BATTERY_PLUGGED_WIRELESS = 4;
- }
enum BatteryStatus {
BATTERY_STATUS_INVALID = 0;
BATTERY_STATUS_UNKNOWN = 1;
@@ -49,7 +45,7 @@ message BatteryServiceDumpProto {
// If true: UPDATES STOPPED -- use 'reset' to restart
optional bool are_updates_stopped = 1;
// Plugged status of power sources
- optional BatteryPlugged plugged = 2;
+ optional android.os.BatteryManagerProto.PlugType plugged = 2;
// Max current in microamperes
optional int32 max_charging_current = 3;
// Max voltage
diff --git a/core/proto/android/view/display.proto b/core/proto/android/view/display.proto
new file mode 100644
index 000000000000..210c6d103faa
--- /dev/null
+++ b/core/proto/android/view/display.proto
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.view;
+
+option java_multiple_files = true;
+
+message DisplayProto {
+ enum DisplayState {
+ // The display state is unknown.
+ DISPLAY_STATE_UNKNOWN = 0;
+ // The display state is off.
+ DISPLAY_STATE_OFF = 1;
+ // The display state is on.
+ DISPLAY_STATE_ON = 2;
+ // The display is dozing in a low power state; it is still on but is
+ // optimized for showing system-provided content while the device is
+ // non-interactive.
+ DISPLAY_STATE_DOZE = 3;
+ // The display is dozing in a suspended low power state; it is still on
+ // but is optimized for showing static system-provided content while the
+ // device is non-interactive.
+ DISPLAY_STATE_DOZE_SUSPEND = 4;
+ // The display is on and optimized for VR mode.
+ DISPLAY_STATE_VR = 5;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 105bb7ef9f3f..507a431e8a28 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -183,15 +183,23 @@
<protected-broadcast
android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST" />
+ <protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT" />
+ <protected-broadcast
android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.input.profile.action.IDLE_TIME_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
<protected-broadcast
- android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" />
diff --git a/core/res/res/layout/magnifier.xml b/core/res/res/layout/magnifier.xml
index 181e5e54bb00..f3344c7470a1 100644
--- a/core/res/res/layout/magnifier.xml
+++ b/core/res/res/layout/magnifier.xml
@@ -18,10 +18,18 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="?android:attr/floatingToolbarPopupBackgroundDrawable">
- <ImageView
- android:id="@+id/magnifier_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="wrap_content">
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/magnifier_inner"
+ android:layout_width="@android:dimen/magnifier_width"
+ android:layout_height="@android:dimen/magnifier_height"
+ android:elevation="@android:dimen/magnifier_elevation"
+ android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
+ android:scaleType="fitXY">
+ <ImageView
+ android:id="@+id/magnifier_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5a497acd38eb..f5b391ec6fee 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3654,7 +3654,7 @@
</declare-styleable>
<!-- Specify one or more <code>t3tPmm-filter</code> elements inside a
- <code>host-nfcf-service</code> element to specify a LF_T3T_PMM -->
+ <code>host-nfcf-service</code> element to specify a LF_T3T_PMM. -->
<declare-styleable name="T3tPmmFilter">
<attr name="name" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0e9028708d66..be0f6d9ac647 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1334,7 +1334,7 @@
split that contains the defined component. -->
<attr name="splitName" format="string" />
- <!-- Specifies the target sandbox this app wants to use. Higher sanbox versions
+ <!-- Specifies the target sandbox this app wants to use. Higher sandbox versions
will have increasing levels of security.
<p>The default value of this attribute is <code>1</code>. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 14069e779939..dc75ba6077e7 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -521,10 +521,11 @@
<dimen name="content_rect_bottom_clip_allowance">20dp</dimen>
<!-- Magnifier dimensions -->
- <dimen name="magnifier_width">200dp</dimen>
+ <dimen name="magnifier_width">164dp</dimen>
<dimen name="magnifier_height">48dp</dimen>
<dimen name="magnifier_elevation">2dp</dimen>
<dimen name="magnifier_offset">42dp</dimen>
+ <item type="dimen" format="float" name="magnifier_zoom_scale">1.25</item>
<dimen name="chooser_grid_padding">0dp</dimen>
<!-- Spacing around the background change frome service to non-service -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 46a6e6efca45..896de53cbc4e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2479,10 +2479,12 @@
<!-- Magnifier -->
<java-symbol type="id" name="magnifier_image" />
+ <java-symbol type="id" name="magnifier_inner" />
<java-symbol type="layout" name="magnifier" />
<java-symbol type="dimen" name="magnifier_width" />
<java-symbol type="dimen" name="magnifier_height" />
<java-symbol type="dimen" name="magnifier_elevation" />
+ <java-symbol type="dimen" name="magnifier_zoom_scale" />
<java-symbol type="dimen" name="magnifier_offset" />
<java-symbol type="string" name="date_picker_prev_month_button" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
index fbaf0f324ab1..0806fa0b9879 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerStressTestRunner.java
@@ -20,7 +20,6 @@ import android.os.Bundle;
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
-import com.android.connectivitymanagertest.stress.WifiApStress;
import com.android.connectivitymanagertest.stress.WifiStressTest;
import junit.framework.TestSuite;
@@ -35,7 +34,7 @@ import junit.framework.TestSuite;
*/
public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunner {
- private int mSoftApIterations = 100;
+ private int mSoftApIterations = 0;
private int mScanIterations = 100;
private int mReconnectIterations = 100;
// sleep time before restart wifi, default is set to 2 minutes
@@ -47,7 +46,6 @@ public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunn
@Override
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
- suite.addTestSuite(WifiApStress.class);
suite.addTestSuite(WifiStressTest.class);
return suite;
}
@@ -60,13 +58,6 @@ public class ConnectivityManagerStressTestRunner extends InstrumentationTestRunn
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- String valueStr = icicle.getString("softap_iterations");
- if (valueStr != null) {
- int iteration = Integer.parseInt(valueStr);
- if (iteration > 0) {
- mSoftApIterations = iteration;
- }
- }
String scanIterationStr = icicle.getString("scan_iterations");
if (scanIterationStr != null) {
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index 64fed7f77176..3706e4b3d8e8 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -129,12 +129,6 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase {
// Get an instance of WifiManager
mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
- if (mWifiManager.isWifiApEnabled()) {
- // if soft AP is enabled, disable it
- mWifiManager.setWifiApEnabled(null, false);
- logv("Disable soft ap");
- }
-
// register a connectivity receiver for CONNECTIVITY_ACTION;
mConnectivityReceiver = new ConnectivityReceiver();
mContext.registerReceiver(mConnectivityReceiver,
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
index 0e57a006eed0..746cb841da66 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
@@ -19,7 +19,6 @@ package com.android.connectivitymanagertest;
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
import com.android.connectivitymanagertest.unit.WifiClientTest;
-import com.android.connectivitymanagertest.unit.WifiSoftAPTest;
import junit.framework.TestSuite;
@@ -35,7 +34,6 @@ public class ConnectivityManagerUnitTestRunner extends InstrumentationTestRunner
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(WifiClientTest.class);
- suite.addTestSuite(WifiSoftAPTest.class);
return suite;
}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
deleted file mode 100644
index de934b9c9d82..000000000000
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.connectivitymanagertest.stress;
-
-
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.AuthAlgorithm;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiManager;
-import android.os.Environment;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner;
-import com.android.connectivitymanagertest.ConnectivityManagerTestBase;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-
-/**
- * Stress test setting up device as wifi hotspot
- */
-public class WifiApStress extends ConnectivityManagerTestBase {
- private static String NETWORK_ID = "AndroidAPTest";
- private static String PASSWD = "androidwifi";
- private final static String OUTPUT_FILE = "WifiStressTestOutput.txt";
- private int mTotalIterations;
- private BufferedWriter mOutputWriter = null;
- private int mLastIteration = 0;
- private boolean mWifiOnlyFlag;
-
- public WifiApStress() {
- super(WifiApStress.class.getSimpleName());
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- ConnectivityManagerStressTestRunner mRunner =
- (ConnectivityManagerStressTestRunner)getInstrumentation();
- mTotalIterations = mRunner.getSoftApInterations();
- mWifiOnlyFlag = mRunner.isWifiOnly();
- turnScreenOn();
- }
-
- @Override
- protected void tearDown() throws Exception {
- // write the total number of iterations into output file
- mOutputWriter = new BufferedWriter(new FileWriter(new File(
- Environment.getExternalStorageDirectory(), OUTPUT_FILE)));
- mOutputWriter.write(String.format("iteration %d out of %d\n",
- mLastIteration + 1, mTotalIterations));
- mOutputWriter.flush();
- mOutputWriter.close();
- super.tearDown();
- }
-
- @LargeTest
- public void testWifiHotSpot() {
- if (mWifiOnlyFlag) {
- logv(getName() + " is excluded for wi-fi only test");
- return;
- }
- WifiConfiguration config = new WifiConfiguration();
- config.SSID = NETWORK_ID;
- config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
- config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
- config.preSharedKey = PASSWD;
-
- // if wifiap enabled, disable it
- assertTrue("failed to disable wifi hotspot",
- mWifiManager.setWifiApEnabled(config, false));
- assertTrue("wifi hotspot not enabled", waitForWifiApState(
- WifiManager.WIFI_AP_STATE_DISABLED, 2 * LONG_TIMEOUT));
-
- // if Wifi is enabled, disable it
- if (mWifiManager.isWifiEnabled()) {
- assertTrue("failed to disable wifi", disableWifi());
- // wait for the wifi state to be DISABLED
- assertTrue("wifi state not disabled", waitForWifiState(
- WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT));
- }
- int i;
- for (i = 0; i < mTotalIterations; i++) {
- logv("iteration: " + i);
- mLastIteration = i;
- // enable Wifi tethering
- assertTrue("failed to enable wifi hotspot",
- mWifiManager.setWifiApEnabled(config, true));
- // wait for wifi ap state to be ENABLED
- assertTrue("wifi hotspot not enabled", waitForWifiApState(
- WifiManager.WIFI_AP_STATE_ENABLED, 2 * LONG_TIMEOUT));
- // wait for wifi tethering result
- assertTrue("tether state not changed", waitForTetherStateChange(LONG_TIMEOUT));
- // allow the wifi tethering to be enabled for 10 seconds
- try {
- Thread.sleep(2 * SHORT_TIMEOUT);
- } catch (Exception e) {
- // ignore
- }
- assertTrue("no uplink data connection after Wi-Fi tethering", pingTest());
- // disable wifi hotspot
- assertTrue("failed to disable wifi hotspot",
- mWifiManager.setWifiApEnabled(config, false));
- assertTrue("wifi hotspot not enabled", waitForWifiApState(
- WifiManager.WIFI_AP_STATE_DISABLED, 2 * LONG_TIMEOUT));
- assertFalse("wifi hotspot still enabled", mWifiManager.isWifiApEnabled());
- }
- }
-
-}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java
deleted file mode 100644
index f202862f6dbf..000000000000
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.connectivitymanagertest.unit;
-
-import android.content.Context;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.AndroidTestCase;
-
-import android.util.Log;
-
-/**
- * Test Wifi soft AP configuration
- */
-public class WifiSoftAPTest extends AndroidTestCase {
-
- private WifiManager mWifiManager;
- private WifiConfiguration mWifiConfig = null;
- private final String TAG = "WifiSoftAPTest";
- private final int DURATION = 10000;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
- assertNotNull(mWifiManager);
- assertTrue(mWifiManager.setWifiApEnabled(null, true));
- mWifiConfig = mWifiManager.getWifiApConfiguration();
- if (mWifiConfig != null) {
- Log.v(TAG, "mWifiConfig is " + mWifiConfig.toString());
- } else {
- Log.v(TAG, "mWifiConfig is null.");
- }
- }
-
- @Override
- protected void tearDown() throws Exception {
- Log.v(TAG, "turn off wifi tethering");
- mWifiManager.setWifiApEnabled(null, false);
- super.tearDown();
- }
-
- // Test case 1: Test the soft AP SSID with letters
- @LargeTest
- public void testApSsidWithAlphabet() {
- WifiConfiguration config = new WifiConfiguration();
- config.SSID = "abcdefghijklmnopqrstuvwxyz";
- config.allowedKeyManagement.set(KeyMgmt.NONE);
- mWifiConfig = config;
- assertTrue(mWifiManager.setWifiApEnabled(mWifiConfig, true));
- try {
- Thread.sleep(DURATION);
- } catch (InterruptedException e) {
- Log.v(TAG, "exception " + e.getStackTrace());
- assertFalse(true);
- }
- assertNotNull(mWifiManager.getWifiApConfiguration());
- assertEquals("wifi AP state is not enabled", WifiManager.WIFI_AP_STATE_ENABLED,
- mWifiManager.getWifiApState());
- }
-}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
index 31ce95eea1d2..4b32ceae0617 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -256,13 +256,13 @@ public class BluetoothStressTest extends InstrumentationTestCase {
mTestUtils.unpair(mAdapter, device);
mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey,
BluetoothTestRunner.sDevicePairPin);
- mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, null);
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, null);
for (int i = 0; i < iterations; i++) {
mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations);
- mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE,
+ mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HID_HOST,
String.format("connectInput(device=%s)", device));
- mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE,
+ mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST,
String.format("disconnectInput(device=%s)", device));
}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index ee159788ad21..ada03666b7ba 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -227,8 +227,8 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.HEADSET:
mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED;
break;
- case BluetoothProfile.INPUT_DEVICE:
- mConnectionAction = BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED;
+ case BluetoothProfile.HID_HOST:
+ mConnectionAction = BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED;
break;
case BluetoothProfile.PAN:
mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED;
@@ -322,8 +322,8 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.HEADSET:
mHeadset = (BluetoothHeadset) proxy;
break;
- case BluetoothProfile.INPUT_DEVICE:
- mInput = (BluetoothInputDevice) proxy;
+ case BluetoothProfile.HID_HOST:
+ mInput = (BluetoothHidHost) proxy;
break;
case BluetoothProfile.PAN:
mPan = (BluetoothPan) proxy;
@@ -342,7 +342,7 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.HEADSET:
mHeadset = null;
break;
- case BluetoothProfile.INPUT_DEVICE:
+ case BluetoothProfile.HID_HOST:
mInput = null;
break;
case BluetoothProfile.PAN:
@@ -362,7 +362,7 @@ public class BluetoothTestUtils extends Assert {
private Context mContext;
private BluetoothA2dp mA2dp = null;
private BluetoothHeadset mHeadset = null;
- private BluetoothInputDevice mInput = null;
+ private BluetoothHidHost mInput = null;
private BluetoothPan mPan = null;
/**
@@ -894,7 +894,7 @@ public class BluetoothTestUtils extends Assert {
* @param adapter The BT adapter.
* @param device The remote device.
* @param profile The profile to connect. One of {@link BluetoothProfile#A2DP},
- * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}.
+ * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}.
* @param methodName The method name to printed in the logs. If null, will be
* "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
*/
@@ -935,8 +935,8 @@ public class BluetoothTestUtils extends Assert {
assertTrue(((BluetoothA2dp)proxy).connect(device));
} else if (profile == BluetoothProfile.HEADSET) {
assertTrue(((BluetoothHeadset)proxy).connect(device));
- } else if (profile == BluetoothProfile.INPUT_DEVICE) {
- assertTrue(((BluetoothInputDevice)proxy).connect(device));
+ } else if (profile == BluetoothProfile.HID_HOST) {
+ assertTrue(((BluetoothHidHost)proxy).connect(device));
}
break;
default:
@@ -975,7 +975,7 @@ public class BluetoothTestUtils extends Assert {
* @param adapter The BT adapter.
* @param device The remote device.
* @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP},
- * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}.
+ * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}.
* @param methodName The method name to printed in the logs. If null, will be
* "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
*/
@@ -1010,8 +1010,8 @@ public class BluetoothTestUtils extends Assert {
assertTrue(((BluetoothA2dp)proxy).disconnect(device));
} else if (profile == BluetoothProfile.HEADSET) {
assertTrue(((BluetoothHeadset)proxy).disconnect(device));
- } else if (profile == BluetoothProfile.INPUT_DEVICE) {
- assertTrue(((BluetoothInputDevice)proxy).disconnect(device));
+ } else if (profile == BluetoothProfile.HID_HOST) {
+ assertTrue(((BluetoothHidHost)proxy).disconnect(device));
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
@@ -1237,7 +1237,7 @@ public class BluetoothTestUtils extends Assert {
long s = System.currentTimeMillis();
while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) {
state = mPan.getConnectionState(device);
- if (state == BluetoothInputDevice.STATE_DISCONNECTED
+ if (state == BluetoothHidHost.STATE_DISCONNECTED
&& (receiver.getFiredFlags() & mask) == mask) {
long finish = receiver.getCompletedTime();
if (start != -1 && finish != -1) {
@@ -1255,7 +1255,7 @@ public class BluetoothTestUtils extends Assert {
int firedFlags = receiver.getFiredFlags();
removeReceiver(receiver);
fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
- methodName, state, BluetoothInputDevice.STATE_DISCONNECTED, firedFlags, mask));
+ methodName, state, BluetoothHidHost.STATE_DISCONNECTED, firedFlags, mask));
}
/**
@@ -1404,7 +1404,7 @@ public class BluetoothTestUtils extends Assert {
String[] actions = {
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED,
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
- BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED};
+ BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED};
ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
expectedFlags);
addReceiver(receiver, actions);
@@ -1443,7 +1443,7 @@ public class BluetoothTestUtils extends Assert {
return mHeadset;
}
break;
- case BluetoothProfile.INPUT_DEVICE:
+ case BluetoothProfile.HID_HOST:
if (mInput != null) {
return mInput;
}
@@ -1469,7 +1469,7 @@ public class BluetoothTestUtils extends Assert {
sleep(POLL_TIME);
}
return mHeadset;
- case BluetoothProfile.INPUT_DEVICE:
+ case BluetoothProfile.HID_HOST:
while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
sleep(POLL_TIME);
}
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 15eab1f72aab..f2eb872893b4 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -19,7 +19,12 @@ LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-Iaidl-files-under, src) \
$(call all-java-files-under, DisabledTestApp/src) \
- $(call all-java-files-under, EnabledTestApp/src)
+ $(call all-java-files-under, EnabledTestApp/src) \
+ $(call all-java-files-under, BinderProxyCountingTestApp/src) \
+ $(call all-java-files-under, BinderProxyCountingTestService/src) \
+ $(call all-Iaidl-files-under, aidl)
+
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
LOCAL_DX_FLAGS := --core-library
LOCAL_JACK_FLAGS := --multi-dex native
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 9c0543b18f8b..51bfc209bf55 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -86,6 +86,7 @@
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.KILL_UID" />
<!-- location test permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
new file mode 100644
index 000000000000..e31d50f15d5f
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
@@ -0,0 +1,27 @@
+# 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_STATIC_JAVA_LIBRARIES := coretests-aidl
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := BinderProxyCountingTestApp
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml
new file mode 100644
index 000000000000..a971730f389d
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.frameworks.coretests.binderproxycountingtestapp">
+
+ <application>
+ <service android:name=".BpcTestAppCmdService"
+ android:exported="true"/>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java b/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java
new file mode 100644
index 000000000000..5aae1203e559
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/src/com/android/frameworks/coretests/binderproxycountingtestapp/BpcTestAppCmdService.java
@@ -0,0 +1,182 @@
+/*
+ * 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.binderproxycountingtestapp;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.frameworks.coretests.aidl.IBinderProxyCountingService;
+import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService;
+import com.android.frameworks.coretests.aidl.ITestRemoteCallback;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class BpcTestAppCmdService extends Service {
+ private static final String TAG = BpcTestAppCmdService.class.getSimpleName();
+
+ private static final String TEST_SERVICE_PKG =
+ "com.android.frameworks.coretests.binderproxycountingtestservice";
+ private static final String TEST_SERVICE_CLASS =
+ TEST_SERVICE_PKG + ".BinderProxyCountingService";
+ private static final int BIND_SERVICE_TIMEOUT_SEC = 5;
+
+ private static ServiceConnection mServiceConnection;
+ private static IBinderProxyCountingService mBpcService;
+
+ private IBpcTestAppCmdService.Stub mBinder = new IBpcTestAppCmdService.Stub() {
+
+ private ArrayList<BroadcastReceiver> mBrList = new ArrayList();
+ private ArrayList<ITestRemoteCallback> mTrcList = new ArrayList();
+
+ @Override
+ public void createSystemBinders(int count) {
+ int i = 0;
+ while (i++ < count) {
+ BroadcastReceiver br = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ }
+ };
+ IntentFilter filt = new IntentFilter(Intent.ACTION_POWER_DISCONNECTED);
+ synchronized (mBrList) {
+ mBrList.add(br);
+ }
+ registerReceiver(br, filt);
+ }
+ }
+
+ @Override
+ public void releaseSystemBinders(int count) {
+ int i = 0;
+ while (i++ < count) {
+ BroadcastReceiver br;
+ synchronized (mBrList) {
+ br = mBrList.remove(0);
+ }
+ unregisterReceiver(br);
+ }
+ }
+
+ @Override
+ public void createTestBinders(int count) {
+ int i = 0;
+ while (i++ < count) {
+ ITestRemoteCallback cb = new ITestRemoteCallback.Stub() {};
+ synchronized (mTrcList) {
+ mTrcList.add(cb);
+ }
+ try {
+ mBpcService.registerCallback(cb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException caught! " + e);
+ }
+ }
+ }
+
+ @Override
+ public void releaseTestBinders(int count) {
+ int i = 0;
+ while (i++ < count) {
+
+ ITestRemoteCallback cb;
+ synchronized (mTrcList) {
+ cb = mTrcList.remove(0);
+ }
+ try {
+ mBpcService.unregisterCallback(cb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException caught! " + e);
+ }
+ }
+ }
+
+ @Override
+ public void releaseAllBinders() {
+ synchronized (mBrList) {
+ while (mBrList.size() > 0) {
+ unregisterReceiver(mBrList.remove(0));
+ }
+ }
+ synchronized (mTrcList) {
+ while (mTrcList.size() > 0) {
+ try {
+ mBpcService.unregisterCallback(mTrcList.remove(0));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException caught! " + e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String bindToTestService() {
+ try {
+ final CountDownLatch bindLatch = new CountDownLatch(1);
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "Service connected");
+ mBpcService = IBinderProxyCountingService.Stub.asInterface(service);
+ bindLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Service disconnected");
+ }
+ };
+ final Intent intent = new Intent()
+ .setComponent(new ComponentName(TEST_SERVICE_PKG, TEST_SERVICE_CLASS));
+ bindService(intent, mServiceConnection,
+ Context.BIND_AUTO_CREATE
+ | Context.BIND_ALLOW_OOM_MANAGEMENT
+ | Context.BIND_NOT_FOREGROUND);
+ if (!bindLatch.await(BIND_SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ throw new RuntimeException("Failed to bind to " + TEST_SERVICE_CLASS);
+ }
+ } catch (Exception e) {
+ unbindFromTestService();
+ Log.e(TAG, e.toString());
+ return e.toString();
+ }
+ return null;
+ }
+
+ @Override
+ public void unbindFromTestService() {
+ if (mBpcService != null) {
+ unbindService(mServiceConnection);
+ }
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+} \ No newline at end of file
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.mk b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
new file mode 100644
index 000000000000..a63cf0e8e8b1
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
@@ -0,0 +1,27 @@
+# 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_STATIC_JAVA_LIBRARIES := coretests-aidl
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := BinderProxyCountingTestService
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml b/core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml
new file mode 100644
index 000000000000..777bd20db212
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?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.frameworks.coretests.binderproxycountingtestservice">
+
+ <application>
+ <service android:name=".BpcTestServiceCmdService"
+ android:exported="true" />
+ <service android:name=".BinderProxyCountingService"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
new file mode 100644
index 000000000000..41b4c69232f4
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
@@ -0,0 +1,53 @@
+/*
+ * 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.binderproxycountingtestservice;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+
+import com.android.frameworks.coretests.aidl.IBinderProxyCountingService;
+import com.android.frameworks.coretests.aidl.ITestRemoteCallback;
+
+public class BinderProxyCountingService extends Service {
+ private static final String TAG = BinderProxyCountingService.class.getSimpleName();
+
+ private IBinderProxyCountingService.Stub mBinder = new IBinderProxyCountingService.Stub() {
+
+ final RemoteCallbackList<ITestRemoteCallback> mTestCallbacks = new RemoteCallbackList<>();
+
+ @Override
+ public void registerCallback(ITestRemoteCallback callback) {
+ synchronized (this) {
+ mTestCallbacks.register(callback);
+ }
+ }
+
+ @Override
+ public void unregisterCallback(ITestRemoteCallback callback) {
+ synchronized (this) {
+ mTestCallbacks.unregister(callback);
+ }
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+} \ No newline at end of file
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java
new file mode 100644
index 000000000000..6bed2a2ec53f
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BpcTestServiceCmdService.java
@@ -0,0 +1,101 @@
+/*
+ * 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.binderproxycountingtestservice;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
+import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService;
+import com.android.internal.os.BinderInternal;
+
+public class BpcTestServiceCmdService extends Service {
+ private static final String TAG = BpcTestServiceCmdService.class.getSimpleName();
+
+ //ServiceThread mHandlerThread;
+ Handler mHandler;
+ HandlerThread mHandlerThread;
+
+ private IBpcTestServiceCmdService.Stub mBinder = new IBpcTestServiceCmdService.Stub() {
+ IBpcCallbackObserver mCallbackObserver;
+
+ @Override
+ public void forceGc() {
+ int gcCount = Integer.parseInt(Debug.getRuntimeStat("art.gc.gc-count"));
+ int i = 20;
+ while (gcCount == Integer.parseInt(Debug.getRuntimeStat("art.gc.gc-count")) && i > 0) {
+ System.gc();
+ System.runFinalization();
+ i--;
+ }
+ }
+
+ @Override
+ public int getBinderProxyCount(int uid) {
+ return BinderInternal.nGetBinderProxyCount(uid);
+ }
+
+ @Override
+ public void setBinderProxyWatermarks(int high, int low) {
+ BinderInternal.nSetBinderProxyCountWatermarks(high, low);
+ }
+
+ @Override
+ public void enableBinderProxyLimit(boolean enable) {
+ BinderInternal.nSetBinderProxyCountEnabled(enable);
+ }
+
+ @Override
+ public void setBinderProxyCountCallback(IBpcCallbackObserver observer) {
+ if (observer != null) {
+ BinderInternal.setBinderProxyCountCallback(
+ new BinderInternal.BinderProxyLimitListener() {
+ @Override
+ public void onLimitReached(int uid) {
+ try {
+ synchronized (observer) {
+ observer.onCallback(uid);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }, mHandler);
+ } else {
+ BinderInternal.clearBinderProxyCountCallback();
+ }
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public void onCreate()
+ {
+ mHandlerThread = new HandlerThread("BinderProxyCountingServiceThread");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+} \ No newline at end of file
diff --git a/core/tests/coretests/aidl/Android.mk b/core/tests/coretests/aidl/Android.mk
new file mode 100644
index 000000000000..86e36b61a5ae
--- /dev/null
+++ b/core/tests/coretests/aidl/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-subdir-Iaidl-files)
+LOCAL_MODULE := coretests-aidl
+include $(BUILD_STATIC_JAVA_LIBRARY) \ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl
new file mode 100644
index 000000000000..a69b0c58233a
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBinderProxyCountingService.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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;
+import com.android.frameworks.coretests.aidl.ITestRemoteCallback;
+
+interface IBinderProxyCountingService {
+ void registerCallback(in ITestRemoteCallback callback);
+ void unregisterCallback(in ITestRemoteCallback callback);
+} \ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.aidl
new file mode 100644
index 000000000000..c4ebd56bed6c
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcCallbackObserver.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 IBpcCallbackObserver {
+ void onCallback(int uid);
+} \ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl
new file mode 100644
index 000000000000..86a0aa0ffc12
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestAppCmdService.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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 IBpcTestAppCmdService {
+ void createSystemBinders(int count);
+ void releaseSystemBinders(int count);
+
+ void createTestBinders(int count);
+ void releaseTestBinders(int count);
+
+ void releaseAllBinders();
+
+ String bindToTestService();
+ void unbindFromTestService();
+} \ No newline at end of file
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl
new file mode 100644
index 000000000000..abdab41ce537
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBpcTestServiceCmdService.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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;
+import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
+
+interface IBpcTestServiceCmdService {
+ void forceGc();
+ int getBinderProxyCount(int uid);
+ void setBinderProxyWatermarks(int high, int low);
+ void enableBinderProxyLimit(boolean enable);
+ void setBinderProxyCountCallback(IBpcCallbackObserver observer);
+} \ No newline at end of file
diff --git a/core/java/android/service/autofill/Dataset.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestRemoteCallback.aidl
index 2342c5f5a45a..36bdb6cc9c41 100644
--- a/core/java/android/service/autofill/Dataset.aidl
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestRemoteCallback.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package android.service.autofill;
+package com.android.frameworks.coretests.aidl;
-parcelable Dataset;
+interface ITestRemoteCallback {
+} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
new file mode 100644
index 000000000000..6cdb35abce5a
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -0,0 +1,378 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+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 com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
+import com.android.frameworks.coretests.aidl.IBpcTestAppCmdService;
+import com.android.frameworks.coretests.aidl.IBpcTestServiceCmdService;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Tests for verifying the Binder Proxy Counting and Limiting.
+ *
+ * To manually build and install relevant test apps
+ *
+ * Build:
+ * mmma frameworks/base/core/tests/coretests/BinderProxyCountingTestApp
+ * mmma frameworks/base/core/tests/coretests/BinderProxyCountingTestService
+ * Install:
+ * adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/BinderProxyCountingTestApp/BinderProxyCountingTestApp.apk
+ * adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/BinderProxyCountingTestService/BinderProxyCountingTestService.apk
+ *
+ * To run the tests, use
+ *
+ * Build: m FrameworksCoreTests
+ * Install: adb install -r \
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * Run: adb shell am instrument -e class android.os.BinderProxyCountingTest -w \
+ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *
+ * or
+ *
+ * bit FrameworksCoreTests:android.os.BinderProxyCountingTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BinderProxyCountingTest {
+ private static final String TAG = BinderProxyCountingTest.class.getSimpleName();
+
+ private static final String TEST_APP_PKG =
+ "com.android.frameworks.coretests.binderproxycountingtestapp";
+ private static final String TEST_APP_CMD_SERVICE = TEST_APP_PKG + ".BpcTestAppCmdService";
+ private static final String TEST_SERVICE_PKG =
+ "com.android.frameworks.coretests.binderproxycountingtestservice";
+ private static final String TEST_SERVICE_CMD_SERVICE =
+ TEST_SERVICE_PKG + ".BpcTestServiceCmdService";
+
+ private static final int BIND_SERVICE_TIMEOUT_SEC = 5;
+ private static final int TOO_MANY_BINDERS_TIMEOUT_SEC = 2;
+
+ // Keep in sync with sBinderProxyCountLimit in BpBinder.cpp
+ private static final int BINDER_PROXY_LIMIT = 2500;
+
+ private static Context sContext;
+ private static UiDevice sUiDevice;
+
+ private static ServiceConnection sTestAppConnection;
+ private static ServiceConnection sTestServiceConnection;
+ private static IBpcTestAppCmdService sBpcTestAppCmdService;
+ private static IBpcTestServiceCmdService sBpcTestServiceCmdService;
+ private static final Intent sTestAppIntent = new Intent()
+ .setComponent(new ComponentName(TEST_APP_PKG, TEST_APP_CMD_SERVICE));
+ private static final Intent sTestServiceIntent = new Intent()
+ .setComponent(new ComponentName(TEST_SERVICE_PKG, TEST_SERVICE_CMD_SERVICE));
+ private static final Consumer<IBinder> sTestAppConsumer = (service) -> {
+ sBpcTestAppCmdService = IBpcTestAppCmdService.Stub.asInterface(service);
+ };
+ private static final Consumer<IBinder> sTestServiceConsumer = (service) -> {
+ sBpcTestServiceCmdService = IBpcTestServiceCmdService.Stub.asInterface(service);
+ };
+ private static int sTestPkgUid;
+
+ /**
+ * Setup any common data for the upcoming tests.
+ */
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ sContext = InstrumentationRegistry.getContext();
+ sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_APP_PKG, 0);
+ ((ActivityManager) sContext.getSystemService(Context.ACTIVITY_SERVICE)).killUid(sTestPkgUid,
+ "Wiping Test Package");
+
+ sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+
+ private ServiceConnection bindService(final Consumer<IBinder> consumer, Intent intent)
+ throws Exception {
+ final CountDownLatch bindLatch = new CountDownLatch(1);
+ ServiceConnection connection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "Service connected");
+ consumer.accept(service);
+ bindLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Service disconnected");
+ }
+ };
+ sContext.bindService(intent, connection,
+ Context.BIND_AUTO_CREATE
+ | Context.BIND_ALLOW_OOM_MANAGEMENT
+ | Context.BIND_NOT_FOREGROUND);
+ if (!bindLatch.await(BIND_SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for the service to bind in " + sTestPkgUid);
+ }
+ return connection;
+ }
+
+
+ private void unbindService(ServiceConnection service) {
+ if (service != null) {
+ sContext.unbindService(service);
+ }
+ }
+
+ private void bindTestAppToTestService() throws Exception {
+ if (sBpcTestAppCmdService != null) {
+ String errorMessage = sBpcTestAppCmdService.bindToTestService();
+ if (errorMessage != null) {
+ fail(errorMessage);
+ }
+ }
+ }
+
+ private void unbindTestAppFromTestService() throws Exception {
+ if (sBpcTestAppCmdService != null) {
+ sBpcTestAppCmdService.unbindFromTestService();
+ }
+ }
+
+ private CountDownLatch createBinderLimitLatch() throws RemoteException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ sBpcTestServiceCmdService.setBinderProxyCountCallback(
+ new IBpcCallbackObserver.Stub() {
+ @Override
+ public void onCallback(int uid) {
+ if (uid == sTestPkgUid) {
+ latch.countDown();
+ }
+ }
+ });
+ return latch;
+ }
+
+ /**
+ * Get the Binder Proxy count held by SYSTEM for a given uid
+ */
+ private int getSystemBinderCount(int uid) throws Exception {
+ return Integer.parseInt(sUiDevice.executeShellCommand(
+ "dumpsys activity binder-proxies " + uid).trim());
+ }
+
+ @Test
+ public void testBinderProxyCount() throws Exception {
+ // Arbitrary list of Binder create and release
+ // Should cumulatively equal 0 and must never add up past the binder limit at any point
+ int[] testValues = {223, -103, -13, 25, 90, -222};
+ try {
+ sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+ // Get the baseline of binders naturally held by the test Package
+ int expectedBinderCount = getSystemBinderCount(sTestPkgUid);
+
+ for (int testValue : testValues) {
+ if (testValue > 0) {
+ sBpcTestAppCmdService.createSystemBinders(testValue);
+ } else {
+ sBpcTestAppCmdService.releaseSystemBinders(-testValue);
+ }
+ expectedBinderCount += testValue;
+ int currentBinderCount = getSystemBinderCount(sTestPkgUid);
+ assertEquals("Current Binder Count (" + currentBinderCount
+ + ") does not equal expected Binder Count (" + expectedBinderCount
+ + ")", expectedBinderCount, currentBinderCount);
+ }
+ } finally {
+ unbindService(sTestAppConnection);
+ }
+ }
+
+ @Test
+ public void testBinderProxyLimitBoundary() throws Exception {
+ final int binderProxyLimit = 2000;
+ final int rearmThreshold = 1800;
+ try {
+ sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+ sTestServiceConnection = bindService(sTestServiceConsumer, sTestServiceIntent);
+ bindTestAppToTestService();
+ sBpcTestServiceCmdService.enableBinderProxyLimit(true);
+
+ sBpcTestServiceCmdService.forceGc();
+ // Get the baseline of binders naturally held by the test Package
+ int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+
+ final CountDownLatch binderLimitLatch = createBinderLimitLatch();
+ sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold);
+
+ // Create Binder Proxies up to the limit
+ sBpcTestAppCmdService.createTestBinders(binderProxyLimit - baseBinderCount);
+ if (binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid
+ + " when proxy limit should not have been reached");
+ }
+
+ // Create one more Binder to cross the limit
+ sBpcTestAppCmdService.createTestBinders(1);
+ if (!binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+ }
+
+ sBpcTestAppCmdService.releaseAllBinders();
+ } finally {
+ unbindTestAppFromTestService();
+ unbindService(sTestAppConnection);
+ unbindService(sTestServiceConnection);
+ }
+ }
+
+ @Test
+ public void testSetBinderProxyLimit() throws Exception {
+ int[] testLimits = {1000, 222, 800};
+ try {
+ sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+ sTestServiceConnection = bindService(sTestServiceConsumer, sTestServiceIntent);
+ bindTestAppToTestService();
+ sBpcTestServiceCmdService.enableBinderProxyLimit(true);
+
+ sBpcTestServiceCmdService.forceGc();
+ int baseBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+ for (int testLimit : testLimits) {
+ final CountDownLatch binderLimitLatch = createBinderLimitLatch();
+ // Change the BinderProxyLimit
+ sBpcTestServiceCmdService.setBinderProxyWatermarks(testLimit, baseBinderCount + 10);
+ // Exceed the new Binder Proxy Limit
+ sBpcTestAppCmdService.createTestBinders(testLimit + 1);
+ if (!binderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+ }
+
+ sBpcTestAppCmdService.releaseTestBinders(testLimit + 1);
+ sBpcTestServiceCmdService.forceGc();
+ }
+ } finally {
+ unbindTestAppFromTestService();
+ unbindService(sTestAppConnection);
+ unbindService(sTestServiceConnection);
+ }
+ }
+
+ @Test
+ public void testRearmCallbackThreshold() throws Exception {
+ final int binderProxyLimit = 2000;
+ final int exceedBinderProxyLimit = binderProxyLimit + 10;
+ final int rearmThreshold = 1800;
+ try {
+ sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+ sTestServiceConnection = bindService(sTestServiceConsumer, sTestServiceIntent);
+ bindTestAppToTestService();
+ sBpcTestServiceCmdService.enableBinderProxyLimit(true);
+
+ sBpcTestServiceCmdService.forceGc();
+ final CountDownLatch firstBinderLimitLatch = createBinderLimitLatch();
+ sBpcTestServiceCmdService.setBinderProxyWatermarks(binderProxyLimit, rearmThreshold);
+ // Exceed the Binder Proxy Limit
+ sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit);
+ if (!firstBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+ }
+
+ sBpcTestServiceCmdService.forceGc();
+ int currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+ // Drop to the threshold, this should not rearm the callback
+ sBpcTestAppCmdService.releaseTestBinders(currentBinderCount - rearmThreshold);
+
+ sBpcTestServiceCmdService.forceGc();
+ currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+
+ final CountDownLatch secondBinderLimitLatch = createBinderLimitLatch();
+ // Exceed the Binder Proxy limit which should not cause a callback since there has
+ // been no rearm
+ sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - currentBinderCount);
+ if (secondBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Received BinderProxyLimitCallback for uid " + sTestPkgUid
+ + " when the callback has not been rearmed yet");
+ }
+
+ sBpcTestServiceCmdService.forceGc();
+ currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+ // Drop below the rearmThreshold to rearm the BinderProxyLimitCallback
+ sBpcTestAppCmdService.releaseTestBinders(currentBinderCount - rearmThreshold + 1);
+
+ sBpcTestServiceCmdService.forceGc();
+ currentBinderCount = sBpcTestServiceCmdService.getBinderProxyCount(sTestPkgUid);
+ // Exceed the Binder Proxy limit for the last time
+ sBpcTestAppCmdService.createTestBinders(exceedBinderProxyLimit - currentBinderCount);
+
+ if (!secondBinderLimitLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for uid " + sTestPkgUid + " to hit limit");
+ }
+ sBpcTestAppCmdService.releaseTestBinders(currentBinderCount);
+ } finally {
+ unbindTestAppFromTestService();
+ unbindService(sTestAppConnection);
+ unbindService(sTestServiceConnection);
+ }
+ }
+
+ @Test
+ public void testKillBadBehavingApp() throws Exception {
+ final CountDownLatch binderDeathLatch = new CountDownLatch(1);
+ final int exceedBinderProxyLimit = BINDER_PROXY_LIMIT + 1;
+
+ try {
+ sTestAppConnection = bindService(sTestAppConsumer, sTestAppIntent);
+ sBpcTestAppCmdService.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.v(TAG, "BpcTestAppCmdService died!");
+ binderDeathLatch.countDown();
+ }
+ }, 0);
+ try {
+ // Exceed the Binder Proxy Limit emulating a bad behaving app
+ sBpcTestAppCmdService.createSystemBinders(exceedBinderProxyLimit);
+ } catch (DeadObjectException doe) {
+ // We are expecting the service to get killed mid call, so a DeadObjectException
+ // is not unexpected
+ }
+
+ if (!binderDeathLatch.await(TOO_MANY_BINDERS_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ sBpcTestAppCmdService.releaseSystemBinders(exceedBinderProxyLimit);
+ fail("Timed out waiting for uid " + sTestPkgUid + " to die.");
+ }
+
+ } finally {
+ unbindService(sTestAppConnection);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
new file mode 100644
index 000000000000..f8d688498b5f
--- /dev/null
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.text;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.filters.Suppress;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextLineTest {
+ private boolean stretchesToFullWidth(CharSequence line) {
+ final TextPaint paint = new TextPaint();
+ final TextLine tl = TextLine.obtain();
+ tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT,
+ Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */);
+ final float originalWidth = tl.metrics(null);
+ final float expandedWidth = 2 * originalWidth;
+
+ tl.justify(expandedWidth);
+ final float newWidth = tl.metrics(null);
+ TextLine.recycle(tl);
+ return Math.abs(newWidth - expandedWidth) < 0.5;
+ }
+
+ @Test
+ public void testJustify_spaces() {
+ // There are no spaces to stretch.
+ assertFalse(stretchesToFullWidth("text"));
+
+ assertTrue(stretchesToFullWidth("one space"));
+ assertTrue(stretchesToFullWidth("exactly two spaces"));
+ assertTrue(stretchesToFullWidth("up to three spaces"));
+ }
+
+ // NBSP should also stretch when it's not used as a base for a combining mark. This doesn't work
+ // yet (b/68204709).
+ @Suppress
+ public void disabledTestJustify_NBSP() {
+ final char nbsp = '\u00A0';
+ assertTrue(stretchesToFullWidth("non-breaking" + nbsp + "space"));
+ assertTrue(stretchesToFullWidth("mix" + nbsp + "and match"));
+
+ final char combining_acute = '\u0301';
+ assertFalse(stretchesToFullWidth("combining" + nbsp + combining_acute + "acute"));
+ }
+}
diff --git a/core/tests/webkit/Android.mk b/core/tests/webkit/Android.mk
new file mode 100644
index 000000000000..e6120ec497e7
--- /dev/null
+++ b/core/tests/webkit/Android.mk
@@ -0,0 +1,44 @@
+# 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)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, unit_tests_src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test
+
+LOCAL_PACKAGE_NAME := WebViewLoadingTests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ WebViewLoadingOnDiskTestApk \
+ WebViewLoadingFromApkTestApk
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/webkit/AndroidManifest.xml b/core/tests/webkit/AndroidManifest.xml
new file mode 100644
index 000000000000..42accdf66891
--- /dev/null
+++ b/core/tests/webkit/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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.webkit.tests"
+ android:sharedUserId="android.uid.system">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.webkit.tests"
+ android:label="Frameworks WebView Loader Tests" />
+
+</manifest>
diff --git a/core/tests/webkit/AndroidTest.xml b/core/tests/webkit/AndroidTest.xml
new file mode 100644
index 000000000000..78cfa462beeb
--- /dev/null
+++ b/core/tests/webkit/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<configuration description="Runs Frameworks WebView Loading Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="WebViewLoadingTests.apk" />
+ <option name="test-file-name" value="WebViewLoadingOnDiskTestApk.apk" />
+ <option name="test-file-name" value="WebViewLoadingFromApkTestApk.apk" />
+ <option name="cleanup-apks" value="true" />
+ <option name="alt-dir" value="out" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.webkit.tests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/core/tests/webkit/apk_with_native_libs/Android.mk b/core/tests/webkit/apk_with_native_libs/Android.mk
new file mode 100644
index 000000000000..7c6c36e0a6a2
--- /dev/null
+++ b/core/tests/webkit/apk_with_native_libs/Android.mk
@@ -0,0 +1,66 @@
+# 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)
+MY_PATH := $(LOCAL_PATH)
+
+# Set shared variables
+MY_MODULE_TAGS := optional
+MY_JNI_SHARED_LIBRARIES := libwebviewtest_jni
+MY_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+MY_SRC_FILES := $(call all-java-files-under, src)
+MY_SDK_VERSION := system_current
+MY_PROGUARD_ENABLED := disabled
+MY_MULTILIB := both
+
+# Recurse down the file tree.
+include $(call all-subdir-makefiles)
+
+
+
+# Builds an apk containing native libraries that will be unzipped on the device.
+include $(CLEAR_VARS)
+
+LOCAL_PATH := $(MY_PATH)
+LOCAL_PACKAGE_NAME := WebViewLoadingOnDiskTestApk
+LOCAL_MANIFEST_FILE := ondisk/AndroidManifest.xml
+
+LOCAL_MODULE_TAGS := $(MY_MODULE_TAGS)
+LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
+LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
+LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
+LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
+LOCAL_MULTILIB := $(MY_MULTILIB)
+
+include $(BUILD_PACKAGE)
+
+
+# Builds an apk containing uncompressed native libraries that have to be
+# accessed through the APK itself on the device.
+include $(CLEAR_VARS)
+
+LOCAL_PATH := $(MY_PATH)
+LOCAL_PACKAGE_NAME := WebViewLoadingFromApkTestApk
+LOCAL_MANIFEST_FILE := inapk/AndroidManifest.xml
+
+LOCAL_MODULE_TAGS := $(MY_MODULE_TAGS)
+LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
+LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
+LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
+LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
+LOCAL_MULTILIB := $(MY_MULTILIB)
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml b/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml
new file mode 100644
index 000000000000..868b2388d135
--- /dev/null
+++ b/core/tests/webkit/apk_with_native_libs/inapk/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.webviewloading_test_from_apk"
+ android:versionCode="1"
+ android:versionName="0.0.0.1">
+
+ <application android:label="WebView Loading Test APK"
+ android:multiArch="true"
+ android:extractNativeLibs="false">
+ <meta-data android:name="com.android.webview.WebViewLibrary"
+ android:value="libwebviewtest_jni.so" />
+ </application>
+</manifest>
diff --git a/core/tests/webkit/apk_with_native_libs/jni/Android.mk b/core/tests/webkit/apk_with_native_libs/jni/Android.mk
new file mode 100644
index 000000000000..fd5b5eb50c5f
--- /dev/null
+++ b/core/tests/webkit/apk_with_native_libs/jni/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 := libwebviewtest_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := WebViewTestJniOnLoad.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_MULTILIB := both
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/core/java/android/service/autofill/SaveInfo.aidl b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
index 8cda608e1814..9b0502fc286d 100644
--- a/core/java/android/service/autofill/SaveInfo.aidl
+++ b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,8 @@
* limitations under the License.
*/
-package android.service.autofill;
+#include <jni.h>
-parcelable SaveInfo;
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ return JNI_VERSION_1_4;
+}
diff --git a/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml b/core/tests/webkit/apk_with_native_libs/ondisk/AndroidManifest.xml
new file mode 100644
index 000000000000..ffffeb8e1630
--- /dev/null
+++ b/core/tests/webkit/apk_with_native_libs/ondisk/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.webviewloading_test_on_disk"
+ android:versionCode="1"
+ android:versionName="0.0.0.1">
+
+ <application android:label="WebView Loading Test APK"
+ android:multiArch="true">
+ <meta-data android:name="com.android.webview.WebViewLibrary"
+ android:value="libwebviewtest_jni.so" />
+ </application>
+</manifest>
diff --git a/core/java/android/view/autofill/AutoFillValue.aidl b/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java
index 05b75622c273..0efa4b4ac694 100644
--- a/core/java/android/view/autofill/AutoFillValue.aidl
+++ b/core/tests/webkit/apk_with_native_libs/src/com/google/android/xts/webview_list/WebViewLoadingTestClass.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-package android.view.autofill;
-// @deprecated TODO(b/35956626): remove once clients use AutofillValue
-parcelable AutoFillValue; \ No newline at end of file
+package com.android.webview.chromium;
+
+/**
+ * An empty class for testing purposes.
+ */
+public class WebViewLoadingTestClass {
+}
diff --git a/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java b/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java
new file mode 100644
index 000000000000..e2f2d37a4d68
--- /dev/null
+++ b/core/tests/webkit/unit_tests_src/com/android/webkit/WebViewLibraryLoaderTest.java
@@ -0,0 +1,329 @@
+/*
+ * 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.webkit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link WebViewLibraryLoader}.
+ * Use the following command to run these tests:
+ * make WebViewLoadingTests \
+ * && adb install -r -d \
+ * ${ANDROID_PRODUCT_OUT}/data/app/WebViewLoadingTests/WebViewLoadingTests.apk \
+ * && adb shell am instrument -e class 'android.webkit.WebViewLibraryLoaderTest' -w \
+ * 'com.android.webkit.tests/android.support.test.runner.AndroidJUnitRunner'
+ */
+@RunWith(AndroidJUnit4.class)
+public final class WebViewLibraryLoaderTest {
+ private static final String WEBVIEW_LIBS_ON_DISK_TEST_APK =
+ "com.android.webviewloading_test_on_disk";
+ private static final String WEBVIEW_LIBS_IN_APK_TEST_APK =
+ "com.android.webviewloading_test_from_apk";
+ private static final String WEBVIEW_LOADING_TEST_NATIVE_LIB = "libwebviewtest_jni.so";
+
+ private PackageInfo webviewOnDiskPackageInfo;
+ private PackageInfo webviewFromApkPackageInfo;
+
+ @Before public void setUp() throws PackageManager.NameNotFoundException {
+ PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ webviewOnDiskPackageInfo =
+ pm.getPackageInfo(WEBVIEW_LIBS_ON_DISK_TEST_APK, PackageManager.GET_META_DATA);
+ webviewFromApkPackageInfo =
+ pm.getPackageInfo(WEBVIEW_LIBS_IN_APK_TEST_APK, PackageManager.GET_META_DATA);
+ }
+
+ private static boolean is64BitDevice() {
+ return Build.SUPPORTED_64_BIT_ABIS.length > 0;
+ }
+
+ // We test the getWebViewNativeLibraryDirectory method here because it handled several different
+ // cases/combinations and it seems unnecessary to create one test-apk for each such combination
+ // and arch.
+
+ /**
+ * Ensure we fetch the correct native library directories in the multi-arch case where
+ * the primary ABI is 64-bit.
+ */
+ @SmallTest
+ @Test public void testGetWebViewLibDirMultiArchPrimary64bit() {
+ final String nativeLib = "nativeLib";
+ final String secondaryNativeLib = "secondaryNativeLib";
+ PackageInfo packageInfo = new PackageInfo();
+ ApplicationInfo ai = new ApplicationInfoBuilder().
+ // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP
+ setPrimaryCpuAbi("arm64-v8a").
+ setNativeLibraryDir(nativeLib).
+ setSecondaryCpuAbi("armeabi").
+ setSecondaryNativeLibraryDir(secondaryNativeLib).
+ create();
+ packageInfo.applicationInfo = ai;
+ String actual32Lib =
+ WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, false /* is64bit */);
+ String actual64Lib =
+ WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, true /* is64bit */);
+ assertEquals(nativeLib, actual64Lib);
+ assertEquals(secondaryNativeLib, actual32Lib);
+ }
+
+ /**
+ * Ensure we fetch the correct native library directory in the 64-bit single-arch case.
+ */
+ @SmallTest
+ @Test public void testGetWebViewLibDirSingleArch64bit() {
+ final String nativeLib = "nativeLib";
+ PackageInfo packageInfo = new PackageInfo();
+ ApplicationInfo ai = new ApplicationInfoBuilder().
+ // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP
+ setPrimaryCpuAbi("arm64-v8a").
+ setNativeLibraryDir(nativeLib).
+ create();
+ packageInfo.applicationInfo = ai;
+ String actual64Lib =
+ WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, true /* is64bit */);
+ assertEquals(nativeLib, actual64Lib);
+ }
+
+ /**
+ * Ensure we fetch the correct native library directory in the 32-bit single-arch case.
+ */
+ @SmallTest
+ @Test public void testGetWebViewLibDirSingleArch32bit() {
+ final String nativeLib = "nativeLib";
+ PackageInfo packageInfo = new PackageInfo();
+ ApplicationInfo ai = new ApplicationInfoBuilder().
+ // See VMRuntime.ABI_TO_INSTRUCTION_SET_MAP
+ setPrimaryCpuAbi("armeabi-v7a").
+ setNativeLibraryDir(nativeLib).
+ create();
+ packageInfo.applicationInfo = ai;
+ String actual32Lib =
+ WebViewLibraryLoader.getWebViewNativeLibraryDirectory(ai, false /* is64bit */);
+ assertEquals(nativeLib, actual32Lib);
+ }
+
+ /**
+ * Ensure we fetch the correct 32-bit library path from an APK with 32-bit and 64-bit
+ * libraries unzipped onto disk.
+ */
+ @MediumTest
+ @Test public void testGetWebViewLibraryPathOnDisk32Bit()
+ throws WebViewFactory.MissingWebViewPackageException {
+ WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewOnDiskPackageInfo, false /* is64bit */);
+ String expectedLibaryDirectory = is64BitDevice() ?
+ webviewOnDiskPackageInfo.applicationInfo.secondaryNativeLibraryDir :
+ webviewOnDiskPackageInfo.applicationInfo.nativeLibraryDir;
+ String lib32Path = expectedLibaryDirectory + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB;
+ assertEquals("Fetched incorrect 32-bit path from WebView library.",
+ lib32Path, actualNativeLib.path);
+ }
+
+ /**
+ * Ensure we fetch the correct 64-bit library path from an APK with 32-bit and 64-bit
+ * libraries unzipped onto disk.
+ */
+ @MediumTest
+ @Test public void testGetWebViewLibraryPathOnDisk64Bit()
+ throws WebViewFactory.MissingWebViewPackageException {
+ // A 32-bit device will not unpack 64-bit libraries.
+ if (!is64BitDevice()) return;
+
+ WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewOnDiskPackageInfo, true /* is64bit */);
+ String lib64Path = webviewOnDiskPackageInfo.applicationInfo.nativeLibraryDir
+ + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB;
+ assertEquals("Fetched incorrect 64-bit path from WebView library.",
+ lib64Path, actualNativeLib.path);
+ }
+
+ /**
+ * Check the size of the 32-bit library fetched from an APK with both 32-bit and 64-bit
+ * libraries unzipped onto disk.
+ */
+ @MediumTest
+ @Test public void testGetWebView32BitLibrarySizeOnDiskIsNonZero()
+ throws WebViewFactory.MissingWebViewPackageException {
+ WebViewLibraryLoader.WebViewNativeLibrary actual32BitNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewOnDiskPackageInfo, false /* is64bit */);
+ assertTrue(actual32BitNativeLib.size > 0);
+ }
+
+ /**
+ * Check the size of the 64-bit library fetched from an APK with both 32-bit and 64-bit
+ * libraries unzipped onto disk.
+ */
+ @MediumTest
+ @Test public void testGetWebView64BitLibrarySizeOnDiskIsNonZero()
+ throws WebViewFactory.MissingWebViewPackageException {
+ // A 32-bit device will not unpack 64-bit libraries.
+ if (!is64BitDevice()) return;
+ WebViewLibraryLoader.WebViewNativeLibrary actual64BitNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewOnDiskPackageInfo, true /* is64bit */);
+ assertTrue(actual64BitNativeLib.size > 0);
+ }
+
+ /**
+ * Ensure we fetch the correct 32-bit library path from an APK with both 32-bit and 64-bit
+ * libraries stored uncompressed in the APK.
+ */
+ @MediumTest
+ @Test public void testGetWebView32BitLibraryPathFromApk()
+ throws WebViewFactory.MissingWebViewPackageException, IOException {
+ WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewFromApkPackageInfo, false /* is64bit */);
+ // The device might have ignored the app's request to not extract native libs, so first
+ // check whether the library paths match those of extracted libraries.
+ String expectedLibaryDirectory = is64BitDevice() ?
+ webviewFromApkPackageInfo.applicationInfo.secondaryNativeLibraryDir :
+ webviewFromApkPackageInfo.applicationInfo.nativeLibraryDir;
+ String lib32Path = expectedLibaryDirectory + "/" + WEBVIEW_LOADING_TEST_NATIVE_LIB;
+ if (lib32Path.equals(actualNativeLib.path)) {
+ // If the libraries were extracted to disk, ensure that they're actually there.
+ assertTrue("The given WebView library doesn't exist.",
+ new File(actualNativeLib.path).exists());
+ } else { // The libraries were not extracted to disk.
+ assertIsValidZipEntryPath(actualNativeLib.path,
+ webviewFromApkPackageInfo.applicationInfo.sourceDir);
+ }
+ }
+
+ /**
+ * Ensure we fetch the correct 32-bit library path from an APK with both 32-bit and 64-bit
+ * libraries stored uncompressed in the APK.
+ */
+ @MediumTest
+ @Test public void testGetWebView64BitLibraryPathFromApk()
+ throws WebViewFactory.MissingWebViewPackageException, IOException {
+ // A 32-bit device will not unpack 64-bit libraries.
+ if (!is64BitDevice()) return;
+
+ WebViewLibraryLoader.WebViewNativeLibrary actualNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewFromApkPackageInfo, true /* is64bit */);
+ assertIsValidZipEntryPath(actualNativeLib.path,
+ webviewFromApkPackageInfo.applicationInfo.sourceDir);
+ }
+
+ private static void assertIsValidZipEntryPath(String path, String zipFilePath)
+ throws IOException {
+ assertTrue("The path to a zip entry must start with the path to the zip file itself."
+ + "Expected zip path: " + zipFilePath + ", actual zip entry: " + path,
+ path.startsWith(zipFilePath + "!/"));
+ String[] pathSplit = path.split("!/");
+ assertEquals("A zip file path should have two parts, the zip path, and the zip entry path.",
+ 2, pathSplit.length);
+ ZipFile zipFile = new ZipFile(pathSplit[0]);
+ assertNotNull("Path doesn't point to a valid zip entry: " + path,
+ zipFile.getEntry(pathSplit[1]));
+ }
+
+
+ /**
+ * Check the size of the 32-bit library fetched from an APK with both 32-bit and 64-bit
+ * libraries stored uncompressed in the APK.
+ */
+ @MediumTest
+ @Test public void testGetWebView32BitLibrarySizeFromApkIsNonZero()
+ throws WebViewFactory.MissingWebViewPackageException {
+ WebViewLibraryLoader.WebViewNativeLibrary actual32BitNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewFromApkPackageInfo, false /* is64bit */);
+ assertTrue(actual32BitNativeLib.size > 0);
+ }
+
+ /**
+ * Check the size of the 64-bit library fetched from an APK with both 32-bit and 64-bit
+ * libraries stored uncompressed in the APK.
+ */
+ @MediumTest
+ @Test public void testGetWebView64BitLibrarySizeFromApkIsNonZero()
+ throws WebViewFactory.MissingWebViewPackageException {
+ // A 32-bit device will not unpack 64-bit libraries.
+ if (!is64BitDevice()) return;
+
+ WebViewLibraryLoader.WebViewNativeLibrary actual64BitNativeLib =
+ WebViewLibraryLoader.getWebViewNativeLibrary(
+ webviewFromApkPackageInfo, true /* is64bit */);
+ assertTrue(actual64BitNativeLib.size > 0);
+ }
+
+ private static class ApplicationInfoBuilder {
+ ApplicationInfo ai;
+
+ public ApplicationInfoBuilder setPrimaryCpuAbi(String primaryCpuAbi) {
+ ai.primaryCpuAbi = primaryCpuAbi;
+ return this;
+ }
+
+ public ApplicationInfoBuilder setSecondaryCpuAbi(String secondaryCpuAbi) {
+ ai.secondaryCpuAbi = secondaryCpuAbi;
+ return this;
+ }
+
+ public ApplicationInfoBuilder setNativeLibraryDir(String nativeLibraryDir) {
+ ai.nativeLibraryDir = nativeLibraryDir;
+ return this;
+ }
+
+ public ApplicationInfoBuilder setSecondaryNativeLibraryDir(
+ String secondaryNativeLibraryDir) {
+ ai.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+ return this;
+ }
+
+ public ApplicationInfoBuilder setMetaData(Bundle metaData) {
+ ai.metaData = metaData;
+ return this;
+ }
+
+ public ApplicationInfoBuilder() {
+ ai = new android.content.pm.ApplicationInfo();
+ }
+
+ public ApplicationInfo create() {
+ return ai;
+ }
+ }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6f2b03838b06..2f9ae578ade1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -24,6 +24,10 @@ applications that come with the platform
<permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
</privapp-permissions>
+ <privapp-permissions package="com.android.apps.tag">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.backupconfirm">
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.CRYPT_KEEPER"/>
@@ -54,6 +58,7 @@ applications that come with the platform
<permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
@@ -353,6 +358,7 @@ applications that come with the platform
<permission name="android.permission.WRITE_DREAM_STATE"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
</privapp-permissions>
<privapp-permissions package="com.android.tv">
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 0e6b31e6ebd9..bec22c934fc6 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -93,6 +93,8 @@ include $(CLEAR_VARS)
LOCAL_MODULE := legacy.test.stubs
LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SDK_VERSION := current
+LOCAL_ADDITIONAL_DEPENDENCIES := $(legacy_test_api_gen_stamp)
# Make sure to run droiddoc first to generate the stub source files.
LOCAL_ADDITIONAL_DEPENDENCIES := $(legacy_test_api_gen_stamp)
@@ -162,3 +164,5 @@ LOCAL_MODULE := legacy-performance-test-hostdex
include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
endif # HOST_OS == linux
+
+legacy_test_api_gen_stamp :=
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 5fc5cb275741..6c606f738aaf 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -105,8 +105,8 @@ static void layerDestroyedVkContext(Layer* layer) {
}
void RenderState::onVkContextDestroyed() {
- mLayerPool->clear();
std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext);
+ destroyLayersInUpdater();
GpuMemoryTracker::onGpuContextDestroyed();
}
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 0f1ccedeaae2..10be6499417f 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -49,7 +49,7 @@ public:
bool write(uint64_t fieldId, long long val);
bool write(uint64_t fieldId, bool val);
bool write(uint64_t fieldId, std::string val);
- bool write(uint64_t fieldId, const char* val);
+ bool write(uint64_t fieldId, const char* val, size_t size);
/**
* Starts a sub-message write session.
@@ -103,4 +103,4 @@ private:
}
}
-#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H \ No newline at end of file
+#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 15144ac2eb28..9dadf1c20510 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -225,14 +225,13 @@ ProtoOutputStream::write(uint64_t fieldId, string val)
}
bool
-ProtoOutputStream::write(uint64_t fieldId, const char* val)
+ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size)
{
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
- int size = 0;
- while (val[size] != '\0') size++;
switch (fieldId & FIELD_TYPE_MASK) {
case TYPE_STRING:
+ case TYPE_BYTES:
writeUtf8StringImpl(id, val, size);
return true;
default:
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 25d247deff24..671c57cb6ad9 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -332,6 +332,9 @@ public final class GnssClock implements Parcelable {
/**
* Gets the clock's Drift in nanoseconds per second.
*
+ * <p>This value is the instantaneous time-derivative of the value provided by
+ * {@link #getBiasNanos()}.
+ *
* <p>A positive value indicates that the frequency is higher than the nominal (e.g. GPS master
* clock) frequency. The error estimate for this reported drift is
* {@link #getDriftUncertaintyNanosPerSecond()}.
diff --git a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java
index bbbdf80612d7..8a06cc604f1e 100644
--- a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java
+++ b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java
@@ -26,6 +26,7 @@ import com.android.egg.R;
public class Ocquarium extends Activity {
ImageView mImageView;
+ private OctopusDrawable mOcto;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -43,10 +44,9 @@ public class Ocquarium extends Activity {
bg.addView(mImageView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- final OctopusDrawable octo = new OctopusDrawable(getApplicationContext());
- octo.setSizePx((int) (OctopusDrawable.randfrange(40f,180f) * dp));
- mImageView.setImageDrawable(octo);
- octo.startDrift();
+ mOcto = new OctopusDrawable(getApplicationContext());
+ mOcto.setSizePx((int) (OctopusDrawable.randfrange(40f,180f) * dp));
+ mImageView.setImageDrawable(mOcto);
mImageView.setOnTouchListener(new View.OnTouchListener() {
boolean touching;
@@ -54,24 +54,36 @@ public class Ocquarium extends Activity {
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- if (octo.hitTest(motionEvent.getX(), motionEvent.getY())) {
+ if (mOcto.hitTest(motionEvent.getX(), motionEvent.getY())) {
touching = true;
- octo.stopDrift();
+ mOcto.stopDrift();
}
break;
case MotionEvent.ACTION_MOVE:
if (touching) {
- octo.moveTo(motionEvent.getX(), motionEvent.getY());
+ mOcto.moveTo(motionEvent.getX(), motionEvent.getY());
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
touching = false;
- octo.startDrift();
+ mOcto.startDrift();
break;
}
return true;
}
});
}
+
+ @Override
+ protected void onPause() {
+ mOcto.stopDrift();
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mOcto.startDrift();
+ }
}
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
new file mode 100644
index 000000000000..4211c272b774
--- /dev/null
+++ b/packages/SettingsLib/OWNERS
@@ -0,0 +1,21 @@
+# People who can approve changes for submission
+asapperstein@google.com
+asargent@google.com
+dehboxturtle@google.com
+dhnishi@google.com
+dling@google.com
+dsandler@google.com
+evanlaird@google.com
+jackqdyulei@google.com
+jmonk@google.com
+mfritze@google.com
+nicoya@google.com
+rogerxue@google.com
+virgild@google.com
+zhfan@google.com
+
+# Emergency approvers in case the above are not available
+miket@google.com
+
+# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
+per-file *.xml=* \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 8def03648558..d90386f904ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -12,9 +12,7 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Color;
-import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.os.BatteryManager;
@@ -297,4 +295,9 @@ public class Utils {
}
return defaultDays;
}
+
+ public static boolean isWifiOnly(Context context) {
+ return !context.getSystemService(ConnectivityManager.class)
+ .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index d1621da09a6c..213002fb9726 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -19,7 +19,7 @@ package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothHidHost;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
@@ -35,7 +35,7 @@ public class HidProfile implements LocalBluetoothProfile {
private static final String TAG = "HidProfile";
private static boolean V = true;
- private BluetoothInputDevice mService;
+ private BluetoothHidHost mService;
private boolean mIsProfileReady;
private final LocalBluetoothAdapter mLocalAdapter;
@@ -53,7 +53,7 @@ public class HidProfile implements LocalBluetoothProfile {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (V) Log.d(TAG,"Bluetooth service connected");
- mService = (BluetoothInputDevice) proxy;
+ mService = (BluetoothHidHost) proxy;
// We just bound to the service, so refresh the UI for any connected HID devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
while (!deviceList.isEmpty()) {
@@ -87,7 +87,7 @@ public class HidProfile implements LocalBluetoothProfile {
mDeviceManager = deviceManager;
mProfileManager = profileManager;
adapter.getProfileProxy(context, new InputDeviceServiceListener(),
- BluetoothProfile.INPUT_DEVICE);
+ BluetoothProfile.HID_HOST);
}
public boolean isConnectable() {
@@ -190,7 +190,7 @@ public class HidProfile implements LocalBluetoothProfile {
if (V) Log.d(TAG, "finalize()");
if (mService != null) {
try {
- BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.INPUT_DEVICE,
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_HOST,
mService);
mService = null;
}catch (Throwable t) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 0750dc750051..9cda669379dd 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -21,9 +21,9 @@ import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHidHost;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothMapClient;
-import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
@@ -123,7 +123,7 @@ public class LocalBluetoothProfileManager {
// Always add HID and PAN profiles
mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
addProfile(mHidProfile, HidProfile.NAME,
- BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
+ BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
mPanProfile = new PanProfile(context);
addPanProfile(mPanProfile, PanProfile.NAME,
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java
new file mode 100644
index 000000000000..a78440c271c9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java
@@ -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 com.android.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.os.UserManager;
+
+import com.android.settingslib.Utils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public abstract class AbstractSimStatusImeiInfoPreferenceController
+ extends AbstractPreferenceController {
+ public AbstractSimStatusImeiInfoPreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mContext.getSystemService(UserManager.class).isAdminUser()
+ && !Utils.isWifiOnly(mContext);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index 817989ab96ae..f4c9bb31f01a 100755
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -415,8 +415,8 @@ public class BatteryMeterDrawableBase extends Drawable {
: (mLevel == 100 ? 0.38f : 0.5f)));
mTextHeight = -mTextPaint.getFontMetrics().ascent;
pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level);
- pctX = mWidth * 0.5f;
- pctY = (mHeight + mTextHeight) * 0.47f;
+ pctX = mWidth * 0.5f + left;
+ pctY = (mHeight + mTextHeight) * 0.47f + top;
pctOpaque = levelTop > pctY;
if (!pctOpaque) {
mTextPath.reset();
@@ -439,8 +439,8 @@ public class BatteryMeterDrawableBase extends Drawable {
if (!mCharging && !mPowerSaveEnabled) {
if (level <= mCriticalLevel) {
// draw the warning text
- final float x = mWidth * 0.5f;
- final float y = (mHeight + mWarningTextHeight) * 0.48f;
+ final float x = mWidth * 0.5f + left;
+ final float y = (mHeight + mWarningTextHeight) * 0.48f + top;
c.drawText(mWarningString, x, y, mWarningTextPaint);
} else if (pctOpaque) {
// draw the percentage text
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
new file mode 100644
index 000000000000..a0e28baee3f6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
@@ -0,0 +1,5 @@
+# Default reviewers for this and subdirectories.
+takaoka@google.com
+yukawa@google.com
+
+# Emergency approvers in case the above are not available \ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
new file mode 100644
index 000000000000..28409fae45e6
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.net.ConnectivityManager;
+import android.os.UserManager;
+import android.util.SparseBooleanArray;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+ shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class,
+ SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class})
+public class SimStatusImeiInfoPreferenceControllerTest {
+
+ private AbstractSimStatusImeiInfoPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mController = new AbstractSimStatusImeiInfoPreferenceController(
+ RuntimeEnvironment.application) {
+ @Override
+ public String getPreferenceKey() {
+ return null;
+ }
+ };
+ }
+
+ @Test
+ public void testIsAvailable_isAdminAndHasMobile_shouldReturnTrue() {
+ ShadowUserManager userManager =
+ extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
+ userManager.setIsAdminUser(true);
+ ShadowConnectivityManager connectivityManager =
+ extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class));
+ connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void testIsAvailable_isAdminButNoMobile_shouldReturnFalse() {
+ ShadowUserManager userManager =
+ extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
+ userManager.setIsAdminUser(true);
+ ShadowConnectivityManager connectivityManager =
+ extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class));
+ connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void testIsAvailable_isNotAdmin_shouldReturnFalse() {
+ ShadowUserManager userManager =
+ extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
+ userManager.setIsAdminUser(false);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Implements(UserManager.class)
+ public static class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
+
+ private boolean mAdminUser;
+
+ public void setIsAdminUser(boolean isAdminUser) {
+ mAdminUser = isAdminUser;
+ }
+
+ @Implementation
+ public boolean isAdminUser() {
+ return mAdminUser;
+ }
+ }
+
+ @Implements(ConnectivityManager.class)
+ public static class ShadowConnectivityManager
+ extends org.robolectric.shadows.ShadowConnectivityManager {
+
+ private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
+
+ public void setNetworkSupported(int networkType, boolean supported) {
+ mSupportedNetworkTypes.put(networkType, supported);
+ }
+
+ @Implementation
+ public boolean isNetworkSupported(int networkType) {
+ return mSupportedNetworkTypes.get(networkType);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
new file mode 100644
index 000000000000..3522b8abfe62
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.settingslib.graph;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.testutils.shadow.SettingsLibShadowResources;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+ shadows = SettingsLibShadowResources.class)
+public class BatteryMeterDrawableBaseTest {
+ private static final int PADDING = 5;
+ private static final int HEIGHT = 80;
+ private static final int WIDTH = 40;
+ @Mock
+ private Canvas mCanvas;
+ private Context mContext;
+ private BatteryMeterDrawableBase mBatteryMeterDrawableBase;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mBatteryMeterDrawableBase = new BatteryMeterDrawableBase(mContext, 0 /* frameColor */);
+ }
+
+ @Test
+ public void testDraw_hasPaddingAndBounds_drawWarningInCorrectPosition() {
+ mBatteryMeterDrawableBase.setPadding(PADDING, PADDING, PADDING, PADDING);
+ mBatteryMeterDrawableBase.setBounds(0, 0, WIDTH + 2 * PADDING, HEIGHT + 2 * PADDING);
+ mBatteryMeterDrawableBase.setBatteryLevel(3);
+
+ mBatteryMeterDrawableBase.draw(mCanvas);
+
+ // WIDTH * 0.5 + PADDING = 25
+ // (HEIGHT + TEXT_HEIGHT) * 0.48 + PADDING = 43.3999998
+ verify(mCanvas).drawText(eq("!"), eq(25f), eq(43.399998f), any(Paint.class));
+ }
+
+ @Test
+ public void testDraw_hasPaddingAndBounds_drawBatteryLevelInCorrectPosition() {
+ mBatteryMeterDrawableBase.setPadding(PADDING, PADDING, PADDING, PADDING);
+ mBatteryMeterDrawableBase.setBounds(0, 0, WIDTH + 2 * PADDING, HEIGHT + 2 * PADDING);
+ mBatteryMeterDrawableBase.setBatteryLevel(20);
+ mBatteryMeterDrawableBase.setShowPercent(true);
+
+ mBatteryMeterDrawableBase.draw(mCanvas);
+
+ // WIDTH * 0.5 + PADDING = 25
+ // (HEIGHT + TEXT_HEIGHT) * 0.47 + PADDING = 42.6
+ verify(mCanvas).drawText(eq("20"), eq(25f), eq(42.6f), any(Paint.class));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index 8869e8dd3821..ddd991023273 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -185,10 +185,10 @@ public class QSDetailItems extends FrameLayout {
}
view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
- if (item.iconDrawable != null) {
- iv.setImageDrawable(item.iconDrawable.getDrawable(iv.getContext()));
+ if (item.icon != null) {
+ iv.setImageDrawable(item.icon.getDrawable(iv.getContext()));
} else {
- iv.setImageResource(item.icon);
+ iv.setImageResource(item.iconResId);
}
iv.getOverlay().clear();
if (item.overlay != null) {
@@ -258,8 +258,8 @@ public class QSDetailItems extends FrameLayout {
}
public static class Item {
- public int icon;
- public QSTile.Icon iconDrawable;
+ public int iconResId;
+ public QSTile.Icon icon;
public Drawable overlay;
public CharSequence line1;
public CharSequence line2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index bc3ccb41cce0..1aecdceb378c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -134,7 +134,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
if (lastDevice != null) {
int batteryLevel = lastDevice.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- state.icon = new BluetoothBatteryDrawable(batteryLevel,
+ state.icon = new BluetoothBatteryTileIcon(batteryLevel,
mContext.getResources().getFraction(
R.fraction.bt_battery_scale_fraction, 1, 1));
}
@@ -212,15 +212,11 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
return new BluetoothDetailAdapter();
}
- private class BluetoothBatteryDrawable extends Icon {
+ private class BluetoothBatteryTileIcon extends Icon {
private int mLevel;
private float mIconScale;
- BluetoothBatteryDrawable(int level) {
- this(level, 1 /* iconScale */);
- }
-
- BluetoothBatteryDrawable(int level, float iconScale) {
+ BluetoothBatteryTileIcon(int level, float iconScale) {
mLevel = level;
mIconScale = iconScale;
}
@@ -302,15 +298,16 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
for (CachedBluetoothDevice device : devices) {
if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
final Item item = new Item();
- item.icon = R.drawable.ic_qs_bluetooth_on;
+ item.iconResId = R.drawable.ic_qs_bluetooth_on;
item.line1 = device.getName();
item.tag = device;
int state = device.getMaxConnectionState();
if (state == BluetoothProfile.STATE_CONNECTED) {
- item.icon = R.drawable.ic_qs_bluetooth_connected;
+ item.iconResId = R.drawable.ic_qs_bluetooth_connected;
int batteryLevel = device.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- item.iconDrawable = new BluetoothBatteryDrawable(batteryLevel);
+ item.icon = new BluetoothBatteryTileIcon(batteryLevel,
+ 1 /* iconScale */);
item.line2 = mContext.getString(
R.string.quick_settings_connected_battery_level,
Utils.formatPercentage(batteryLevel));
@@ -321,7 +318,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
items.add(connectedDevices, item);
connectedDevices++;
} else if (state == BluetoothProfile.STATE_CONNECTING) {
- item.icon = R.drawable.ic_qs_bluetooth_connecting;
+ item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
item.line2 = mContext.getString(R.string.quick_settings_connecting);
items.add(connectedDevices, item);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index fb396b9ddb07..678aa7116f73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -19,26 +19,17 @@ package com.android.systemui.qs.tiles;
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
import android.app.Dialog;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.MediaRouter;
-import android.os.UserHandle;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.Log;
-import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.Button;
-import com.android.internal.app.MediaRouteChooserDialog;
-import com.android.internal.app.MediaRouteControllerDialog;
import com.android.internal.app.MediaRouteDialogPresenter;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -280,7 +271,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
for (CastDevice device : devices) {
if (device.state == CastDevice.STATE_CONNECTED) {
final Item item = new Item();
- item.icon = R.drawable.ic_qs_cast_on;
+ item.iconResId = R.drawable.ic_qs_cast_on;
item.line1 = getDeviceName(device);
item.line2 = mContext.getString(R.string.quick_settings_connected);
item.tag = device;
@@ -300,7 +291,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
final CastDevice device = mVisibleOrder.get(id);
if (!devices.contains(device)) continue;
final Item item = new Item();
- item.icon = R.drawable.ic_qs_cast_off;
+ item.iconResId = R.drawable.ic_qs_cast_off;
item.line1 = getDeviceName(device);
if (device.state == CastDevice.STATE_CONNECTING) {
item.line2 = mContext.getString(R.string.quick_settings_connecting);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 23702736b0db..977a725829bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -402,7 +402,7 @@ public class WifiTile extends QSTileImpl<SignalState> {
final AccessPoint ap = mAccessPoints[i];
final Item item = new Item();
item.tag = ap;
- item.icon = mWifiController.getIcon(ap);
+ item.iconResId = mWifiController.getIcon(ap);
item.line1 = ap.getSsid();
item.line2 = ap.isActive() ? ap.getSummary() : null;
item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 68fe9a83c707..84b7015f474e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -29,7 +29,6 @@ import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
-import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -173,12 +172,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private int mOverrideTint;
private float mOverrideAmount;
private boolean mShadowHidden;
- private boolean mWasActivatedOnDown;
/**
* Similar to mDimmed but is also true if it's not dimmable but should be
*/
private boolean mNeedsDimming;
private int mDimmedAlpha;
+ private boolean mBlockNextTouch;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -204,7 +203,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
} else {
makeInactive(true /* animate */);
}
- }, this::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+ }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
}
@Override
@@ -241,9 +240,15 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mNeedsDimming && !mActivated && ev.getActionMasked() == MotionEvent.ACTION_DOWN
+ if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
&& disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
- return true;
+ if (!mActivated) {
+ return true;
+ } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
+ mBlockNextTouch = true;
+ makeInactive(true /* animate */);
+ return true;
+ }
}
return super.onInterceptTouchEvent(ev);
}
@@ -263,10 +268,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result;
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- mWasActivatedOnDown = mActivated;
+ if (mBlockNextTouch) {
+ mBlockNextTouch = false;
+ return false;
}
- if ((mNeedsDimming && !mActivated) && !isTouchExplorationEnabled() && isInteractive()) {
+ if (mNeedsDimming && !isTouchExplorationEnabled() && isInteractive()) {
boolean wasActivated = mActivated;
result = handleTouchEventDimmed(event);
if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
@@ -312,7 +318,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public boolean performClick() {
- if (mWasActivatedOnDown || !mNeedsDimming || isTouchExplorationEnabled()) {
+ if (!mNeedsDimming || isTouchExplorationEnabled()) {
return super.performClick();
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
index dcb6a3801844..0d62703cbced 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
@@ -142,7 +142,7 @@ public class DoubleTapHelper {
&& Math.abs(event.getY() - mDownY) < mTouchSlop;
}
- private boolean isWithinDoubleTapSlop(MotionEvent event) {
+ public boolean isWithinDoubleTapSlop(MotionEvent event) {
if (!mActivated) {
// If we're not activated there's no double tap slop to satisfy.
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index d24e51c73151..40ee8386e895 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -382,13 +382,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... args) {
- // Disable tethering if enabling Wifi
- final int wifiApState = mWifiManager.getWifiApState();
- if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
- (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
- mWifiManager.setWifiApEnabled(null, false);
- }
-
mWifiManager.setWifiEnabled(enabled);
return null;
}
diff --git a/proto/Android.bp b/proto/Android.bp
new file mode 100644
index 000000000000..95f453c3e523
--- /dev/null
+++ b/proto/Android.bp
@@ -0,0 +1,17 @@
+java_library_static {
+ name: "framework-protos",
+ host_supported: true,
+ proto: {
+ type: "nano",
+ },
+ srcs: ["src/**/*.proto"],
+ no_framework_libs: true,
+ target: {
+ android: {
+ jarjar_rules: "jarjar-rules.txt",
+ },
+ host: {
+ static_libs: ["libprotobuf-java-nano"],
+ },
+ },
+}
diff --git a/proto/Android.mk b/proto/Android.mk
deleted file mode 100644
index 1c03d1616f9f..000000000000
--- a/proto/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := framework-protos
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_SRC_FILES:= $(call all-proto-files-under, src)
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Host-side version of framework-protos
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := host-framework-protos
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_SRC_FILES:= $(call all-proto-files-under, src)
-
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_STATIC_JAVA_LIBRARIES := host-libprotobuf-java-nano
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
-
-include $(BUILD_HOST_JAVA_LIBRARY) \ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 8e8835959084..93aa5207a9ec 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4562,6 +4562,11 @@ message MetricsEvent {
// OS: O MR
NOTIFICATION_SNOOZE_OPTIONS = 1142;
+ // OPEN: Settings > Display > Colors
+ // CATEGORY: SETTINGS
+ // OS: O MR
+ COLOR_MODE_SETTINGS = 1143;
+
// ---- End O-MR1 Constants, all O-MR1 constants go above this line ----
// OPEN: Settings > Network & Internet > Mobile network
@@ -4699,6 +4704,11 @@ message MetricsEvent {
// OS: P
AUTOFILL_SAVE_EXPLICITLY_TRIGGERED = 1229;
+ // OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
+ // CATEGORY: SETTINGS
+ // OS: P
+ WIFI_CALLING_FOR_SUB = 1230;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 76e77825b227..6c154389526f 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -107,8 +107,6 @@ import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
import com.android.server.policy.IconUtilities;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -174,21 +172,27 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
Slog.i(TAG, "Received broadcast: " + action + " on user " + userId);
}
- if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- onConfigurationChanged();
- } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
- || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
- synchronized (mLock) {
- reloadWidgetsMaskedState(userId);
- }
- } else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) {
- String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- updateWidgetPackageSuspensionMaskedState(packages, true, getSendingUserId());
- } else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) {
- String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- updateWidgetPackageSuspensionMaskedState(packages, false, getSendingUserId());
- } else {
- onPackageBroadcastReceived(intent, userId);
+ switch (action) {
+ case Intent.ACTION_CONFIGURATION_CHANGED:
+ onConfigurationChanged();
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
+ case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+ synchronized (mLock) {
+ reloadWidgetsMaskedState(userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGES_SUSPENDED:
+ onPackageBroadcastReceived(intent, getSendingUserId());
+ updateWidgetPackageSuspensionMaskedState(intent, true, getSendingUserId());
+ break;
+ case Intent.ACTION_PACKAGES_UNSUSPENDED:
+ onPackageBroadcastReceived(intent, getSendingUserId());
+ updateWidgetPackageSuspensionMaskedState(intent, false, getSendingUserId());
+ break;
+ default:
+ onPackageBroadcastReceived(intent, getSendingUserId());
+ break;
}
}
};
@@ -378,25 +382,32 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
boolean changed = false;
boolean componentsModified = false;
- String pkgList[] = null;
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = true;
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = false;
- } else {
- Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- String pkgName = uri.getSchemeSpecificPart();
- if (pkgName == null) {
- return;
+ final String pkgList[];
+ switch (action) {
+ case Intent.ACTION_PACKAGES_SUSPENDED:
+ case Intent.ACTION_PACKAGES_UNSUSPENDED:
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ changed = true;
+ break;
+ case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+ added = true;
+ // Follow through
+ case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ break;
+ default: {
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ String pkgName = uri.getSchemeSpecificPart();
+ if (pkgName == null) {
+ return;
+ }
+ pkgList = new String[] { pkgName };
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
}
- pkgList = new String[] { pkgName };
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
}
if (pkgList == null || pkgList.length == 0) {
return;
@@ -516,12 +527,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
/**
* Incrementally update the masked state due to package suspension state.
*/
- private void updateWidgetPackageSuspensionMaskedState(String[] packagesArray, boolean suspended,
+ private void updateWidgetPackageSuspensionMaskedState(Intent intent, boolean suspended,
int profileId) {
+ String[] packagesArray = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (packagesArray == null) {
return;
}
- Set<String> packages = new ArraySet<String>(Arrays.asList(packagesArray));
+ Set<String> packages = new ArraySet<>(Arrays.asList(packagesArray));
synchronized (mLock) {
final int N = mProviders.size();
for (int i = 0; i < N; i++) {
@@ -2630,11 +2642,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// No file written for this user - nothing to do.
AtomicFile file = getSavedStateFile(profileId);
- try {
- FileInputStream stream = file.openRead();
+ try (FileInputStream stream = file.openRead()) {
version = readProfileStateFromFileLocked(stream, profileId, loadedWidgets);
- IoUtils.closeQuietly(stream);
- } catch (FileNotFoundException e) {
+ } catch (IOException e) {
Slog.w(TAG, "Failed to read state: " + e);
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f9213aabe272..622b84239711 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2877,7 +2877,7 @@ public class BackupManagerService implements BackupManagerServiceInterface {
// The backend reports that our dataset has been wiped. Note this in
// the event log; the no-success code below will reset the backup
// state as well.
- EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
+ EventLog.writeEvent(EventLogTags.BACKUP_RESET, transportName);
}
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread", e);
@@ -9781,7 +9781,8 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
- EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
+ String transportDirName = transport.transportDirName();
+ EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName);
long startRealtime = SystemClock.elapsedRealtime();
int status = transport.initializeDevice();
@@ -9794,7 +9795,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
Slog.i(TAG, "Device init successful");
int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
- resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
+ resetBackupState(new File(mBaseStateDir, transportDirName));
EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
synchronized (mQueueLock) {
recordInitPendingLocked(false, transportName);
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 7a8a920e7df7..c0caa557b4ae 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -339,7 +339,7 @@ public class PerformBackupTask implements BackupRestoreTask {
// The backend reports that our dataset has been wiped. Note this in
// the event log; the no-success code below will reset the backup
// state as well.
- EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
+ EventLog.writeEvent(EventLogTags.BACKUP_RESET, transportName);
}
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread", e);
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 939b1ae11b95..690922fd9aa9 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -79,7 +79,8 @@ public class PerformInitializeTask implements Runnable {
}
Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
- EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
+ String transportDirName = transport.transportDirName();
+ EventLog.writeEvent(EventLogTags.BACKUP_START, transportDirName);
long startRealtime = SystemClock.elapsedRealtime();
int status = transport.initializeDevice();
@@ -94,7 +95,7 @@ public class PerformInitializeTask implements Runnable {
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
backupManagerService
.resetBackupState(new File(backupManagerService.getBaseStateDir(),
- transport.transportDirName()));
+ transportDirName));
EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
synchronized (backupManagerService.getQueueLock()) {
backupManagerService.recordInitPendingLocked(false, transportName);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 4c08f6290c6c..3904fc96caca 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -63,6 +63,7 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
@@ -190,7 +191,8 @@ class AlarmManagerService extends SystemService {
/**
* For each uid, this is the last time we dispatched an "allow while idle" alarm,
- * used to determine the earliest we can dispatch the next such alarm.
+ * used to determine the earliest we can dispatch the next such alarm. Times are in the
+ * 'elapsed' timebase.
*/
final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
@@ -355,6 +357,22 @@ class AlarmManagerService extends SystemService {
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
pw.println();
}
+
+ void dumpProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(ConstantsProto.MIN_FUTURITY_DURATION_MS, MIN_FUTURITY);
+ proto.write(ConstantsProto.MIN_INTERVAL_DURATION_MS, MIN_INTERVAL);
+ proto.write(ConstantsProto.LISTENER_TIMEOUT_DURATION_MS, LISTENER_TIMEOUT);
+ proto.write(ConstantsProto.ALLOW_WHILE_IDLE_SHORT_DURATION_MS,
+ ALLOW_WHILE_IDLE_SHORT_TIME);
+ proto.write(ConstantsProto.ALLOW_WHILE_IDLE_LONG_DURATION_MS,
+ ALLOW_WHILE_IDLE_LONG_TIME);
+ proto.write(ConstantsProto.ALLOW_WHILE_IDLE_WHITELIST_DURATION_MS,
+ ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+
+ proto.end(token);
+ }
}
final Constants mConstants;
@@ -632,6 +650,20 @@ class AlarmManagerService extends SystemService {
b.append('}');
return b.toString();
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId, long nowElapsed,
+ long nowRTC) {
+ final long token = proto.start(fieldId);
+
+ proto.write(BatchProto.START_REALTIME, start);
+ proto.write(BatchProto.END_REALTIME, end);
+ proto.write(BatchProto.FLAGS, flags);
+ for (Alarm a : alarms) {
+ a.writeToProto(proto, BatchProto.ALARMS, nowElapsed, nowRTC);
+ }
+
+ proto.end(token);
+ }
}
static class BatchTimeOrder implements Comparator<Batch> {
@@ -1007,6 +1039,29 @@ class AlarmManagerService extends SystemService {
+ ", alarmType=" + mAlarmType
+ "}";
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(InFlightProto.UID, mUid);
+ proto.write(InFlightProto.TAG, mTag);
+ proto.write(InFlightProto.WHEN_ELAPSED_MS, mWhenElapsed);
+ proto.write(InFlightProto.ALARM_TYPE, mAlarmType);
+ if (mPendingIntent != null) {
+ mPendingIntent.writeToProto(proto, InFlightProto.PENDING_INTENT);
+ }
+ if (mBroadcastStats != null) {
+ mBroadcastStats.writeToProto(proto, InFlightProto.BROADCAST_STATS);
+ }
+ if (mFilterStats != null) {
+ mFilterStats.writeToProto(proto, InFlightProto.FILTER_STATS);
+ }
+ if (mWorkSource != null) {
+ mWorkSource.writeToProto(proto, InFlightProto.WORK_SOURCE);
+ }
+
+ proto.end(token);
+ }
}
static final class FilterStats {
@@ -1037,6 +1092,20 @@ class AlarmManagerService extends SystemService {
+ ", nesting=" + nesting
+ "}";
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(FilterStatsProto.TAG, mTag);
+ proto.write(FilterStatsProto.LAST_FLIGHT_TIME_REALTIME, lastTime);
+ proto.write(FilterStatsProto.TOTAL_FLIGHT_DURATION_MS, aggregateTime);
+ proto.write(FilterStatsProto.COUNT, count);
+ proto.write(FilterStatsProto.WAKEUP_COUNT, numWakeup);
+ proto.write(FilterStatsProto.START_TIME_REALTIME, startTime);
+ proto.write(FilterStatsProto.NESTING, nesting);
+
+ proto.end(token);
+ }
}
static final class BroadcastStats {
@@ -1067,6 +1136,20 @@ class AlarmManagerService extends SystemService {
+ ", nesting=" + nesting
+ "}";
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(BroadcastStatsProto.UID, mUid);
+ proto.write(BroadcastStatsProto.PACKAGE_NAME, mPackageName);
+ proto.write(BroadcastStatsProto.TOTAL_FLIGHT_DURATION_MS, aggregateTime);
+ proto.write(BroadcastStatsProto.COUNT, count);
+ proto.write(BroadcastStatsProto.WAKEUP_COUNT, numWakeup);
+ proto.write(BroadcastStatsProto.START_TIME_REALTIME, startTime);
+ proto.write(BroadcastStatsProto.NESTING, nesting);
+
+ proto.end(token);
+ }
}
final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
@@ -1128,14 +1211,14 @@ class AlarmManagerService extends SystemService {
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
-
+
// now that we have initied the driver schedule the alarm
mClockReceiver = new ClockReceiver();
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
mInteractiveStateReceiver = new InteractiveStateReceiver();
mUninstallReceiver = new UninstallReceiver();
-
+
if (mNativeData != 0) {
AlarmThread waitThread = new AlarmThread();
waitThread.start();
@@ -1568,7 +1651,12 @@ class AlarmManagerService extends SystemService {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
- dumpImpl(pw);
+
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpImpl(pw);
+ }
}
};
@@ -1681,7 +1769,7 @@ class AlarmManagerService extends SystemService {
pw.print(" Idling until: ");
if (mPendingIdleUntil != null) {
pw.println(mPendingIdleUntil);
- mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf);
+ mPendingIdleUntil.dump(pw, " ", nowELAPSED, nowRTC, sdf);
} else {
pw.println("null");
}
@@ -1691,7 +1779,7 @@ class AlarmManagerService extends SystemService {
if (mNextWakeFromIdle != null) {
pw.println();
pw.print(" Next wake from idle: "); pw.println(mNextWakeFromIdle);
- mNextWakeFromIdle.dump(pw, " ", nowRTC, nowELAPSED, sdf);
+ mNextWakeFromIdle.dump(pw, " ", nowELAPSED, nowRTC, sdf);
}
pw.println();
@@ -1760,6 +1848,7 @@ class AlarmManagerService extends SystemService {
}
};
int len = 0;
+ // Get the top 10 FilterStats, ordered by aggregateTime.
for (int iu=0; iu<mBroadcastStats.size(); iu++) {
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
for (int ip=0; ip<uidStats.size(); ip++) {
@@ -1886,6 +1975,244 @@ class AlarmManagerService extends SystemService {
}
}
+ void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ synchronized (mLock) {
+ final long nowRTC = System.currentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ proto.write(AlarmManagerServiceProto.CURRENT_TIME, nowRTC);
+ proto.write(AlarmManagerServiceProto.ELAPSED_REALTIME, nowElapsed);
+ proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_CLOCK_TIME,
+ mLastTimeChangeClockTime);
+ proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_REALTIME,
+ mLastTimeChangeRealtime);
+
+ mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);
+
+ final int foregroundUidsSize = mForegroundUids.size();
+ for (int i = 0; i < foregroundUidsSize; i++) {
+ if (mForegroundUids.valueAt(i)) {
+ proto.write(AlarmManagerServiceProto.FOREGROUND_UIDS, mForegroundUids.keyAt(i));
+ }
+ }
+ for (String pkg : mForcedAppStandbyPackages) {
+ proto.write(AlarmManagerServiceProto.FORCED_APP_STANDBY_PACKAGES, pkg);
+ }
+
+ proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
+ if (!mInteractive) {
+ // Durations
+ proto.write(AlarmManagerServiceProto.TIME_SINCE_NON_INTERACTIVE_MS,
+ nowElapsed - mNonInteractiveStartTime);
+ proto.write(AlarmManagerServiceProto.MAX_WAKEUP_DELAY_MS,
+ currentNonWakeupFuzzLocked(nowElapsed));
+ proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_DISPATCH_MS,
+ nowElapsed - mLastAlarmDeliveryTime);
+ proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_DELIVERY_MS,
+ nowElapsed - mNextNonWakeupDeliveryTime);
+ }
+
+ proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_ALARM_MS,
+ mNextNonWakeup - nowElapsed);
+ proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_WAKEUP_MS,
+ mNextWakeup - nowElapsed);
+ proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_MS,
+ nowElapsed - mLastWakeup);
+ proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
+ nowElapsed - mLastWakeupSet);
+ proto.write(AlarmManagerServiceProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
+ for (int i : mDeviceIdleUserWhitelist) {
+ proto.write(AlarmManagerServiceProto.DEVICE_IDLE_USER_WHITELIST_APP_IDS, i);
+ }
+
+ final TreeSet<Integer> users = new TreeSet<>();
+ final int nextAlarmClockForUserSize = mNextAlarmClockForUser.size();
+ for (int i = 0; i < nextAlarmClockForUserSize; i++) {
+ users.add(mNextAlarmClockForUser.keyAt(i));
+ }
+ final int pendingSendNextAlarmClockChangedForUserSize =
+ mPendingSendNextAlarmClockChangedForUser.size();
+ for (int i = 0; i < pendingSendNextAlarmClockChangedForUserSize; i++) {
+ users.add(mPendingSendNextAlarmClockChangedForUser.keyAt(i));
+ }
+ for (int user : users) {
+ final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
+ final long time = next != null ? next.getTriggerTime() : 0;
+ final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
+ final long aToken = proto.start(AlarmManagerServiceProto.NEXT_ALARM_CLOCK_METADATA);
+ proto.write(AlarmClockMetadataProto.USER, user);
+ proto.write(AlarmClockMetadataProto.IS_PENDING_SEND, pendingSend);
+ proto.write(AlarmClockMetadataProto.TRIGGER_TIME_MS, time);
+ proto.end(aToken);
+ }
+ for (Batch b : mAlarmBatches) {
+ b.writeToProto(proto, AlarmManagerServiceProto.PENDING_ALARM_BATCHES,
+ nowElapsed, nowRTC);
+ }
+ for (int i = 0; i < mPendingBackgroundAlarms.size(); i++) {
+ final ArrayList<Alarm> blockedAlarms = mPendingBackgroundAlarms.valueAt(i);
+ if (blockedAlarms != null) {
+ for (Alarm a : blockedAlarms) {
+ a.writeToProto(proto,
+ AlarmManagerServiceProto.PENDING_USER_BLOCKED_BACKGROUND_ALARMS,
+ nowElapsed, nowRTC);
+ }
+ }
+ }
+ if (mPendingIdleUntil != null) {
+ mPendingIdleUntil.writeToProto(
+ proto, AlarmManagerServiceProto.PENDING_IDLE_UNTIL, nowElapsed, nowRTC);
+ }
+ for (Alarm a : mPendingWhileIdleAlarms) {
+ a.writeToProto(proto, AlarmManagerServiceProto.PENDING_WHILE_IDLE_ALARMS,
+ nowElapsed, nowRTC);
+ }
+ if (mNextWakeFromIdle != null) {
+ mNextWakeFromIdle.writeToProto(proto, AlarmManagerServiceProto.NEXT_WAKE_FROM_IDLE,
+ nowElapsed, nowRTC);
+ }
+
+ for (Alarm a : mPendingNonWakeupAlarms) {
+ a.writeToProto(proto, AlarmManagerServiceProto.PAST_DUE_NON_WAKEUP_ALARMS,
+ nowElapsed, nowRTC);
+ }
+
+ proto.write(AlarmManagerServiceProto.DELAYED_ALARM_COUNT, mNumDelayedAlarms);
+ proto.write(AlarmManagerServiceProto.TOTAL_DELAY_TIME_MS, mTotalDelayTime);
+ proto.write(AlarmManagerServiceProto.MAX_DELAY_DURATION_MS, mMaxDelayTime);
+ proto.write(AlarmManagerServiceProto.MAX_NON_INTERACTIVE_DURATION_MS,
+ mNonInteractiveTime);
+
+ proto.write(AlarmManagerServiceProto.BROADCAST_REF_COUNT, mBroadcastRefCount);
+ proto.write(AlarmManagerServiceProto.PENDING_INTENT_SEND_COUNT, mSendCount);
+ proto.write(AlarmManagerServiceProto.PENDING_INTENT_FINISH_COUNT, mSendFinishCount);
+ proto.write(AlarmManagerServiceProto.LISTENER_SEND_COUNT, mListenerCount);
+ proto.write(AlarmManagerServiceProto.LISTENER_FINISH_COUNT, mListenerFinishCount);
+
+ for (InFlight f : mInFlight) {
+ f.writeToProto(proto, AlarmManagerServiceProto.OUTSTANDING_DELIVERIES);
+ }
+
+ proto.write(AlarmManagerServiceProto.ALLOW_WHILE_IDLE_MIN_DURATION_MS,
+ mAllowWhileIdleMinTime);
+ for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
+ final long token = proto.start(
+ AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID,
+ mLastAllowWhileIdleDispatch.keyAt(i));
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS,
+ mLastAllowWhileIdleDispatch.valueAt(i));
+ proto.end(token);
+ }
+
+ mLog.writeToProto(proto, AlarmManagerServiceProto.RECENT_PROBLEMS);
+
+ final FilterStats[] topFilters = new FilterStats[10];
+ final Comparator<FilterStats> comparator = new Comparator<FilterStats>() {
+ @Override
+ public int compare(FilterStats lhs, FilterStats rhs) {
+ if (lhs.aggregateTime < rhs.aggregateTime) {
+ return 1;
+ } else if (lhs.aggregateTime > rhs.aggregateTime) {
+ return -1;
+ }
+ return 0;
+ }
+ };
+ int len = 0;
+ // Get the top 10 FilterStats, ordered by aggregateTime.
+ for (int iu = 0; iu < mBroadcastStats.size(); ++iu) {
+ ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
+ for (int ip = 0; ip < uidStats.size(); ++ip) {
+ BroadcastStats bs = uidStats.valueAt(ip);
+ for (int is = 0; is < bs.filterStats.size(); ++is) {
+ FilterStats fs = bs.filterStats.valueAt(is);
+ int pos = len > 0
+ ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0;
+ if (pos < 0) {
+ pos = -pos - 1;
+ }
+ if (pos < topFilters.length) {
+ int copylen = topFilters.length - pos - 1;
+ if (copylen > 0) {
+ System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
+ }
+ topFilters[pos] = fs;
+ if (len < topFilters.length) {
+ len++;
+ }
+ }
+ }
+ }
+ }
+ for (int i = 0; i < len; ++i) {
+ final long token = proto.start(AlarmManagerServiceProto.TOP_ALARMS);
+ FilterStats fs = topFilters[i];
+
+ proto.write(AlarmManagerServiceProto.TopAlarm.UID, fs.mBroadcastStats.mUid);
+ proto.write(AlarmManagerServiceProto.TopAlarm.PACKAGE_NAME,
+ fs.mBroadcastStats.mPackageName);
+ fs.writeToProto(proto, AlarmManagerServiceProto.TopAlarm.FILTER);
+
+ proto.end(token);
+ }
+
+ final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
+ for (int iu = 0; iu < mBroadcastStats.size(); ++iu) {
+ ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
+ for (int ip = 0; ip < uidStats.size(); ++ip) {
+ final long token = proto.start(AlarmManagerServiceProto.ALARM_STATS);
+
+ BroadcastStats bs = uidStats.valueAt(ip);
+ bs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.BROADCAST);
+
+ // uidStats is an ArrayMap, which we can't sort.
+ tmpFilters.clear();
+ for (int is = 0; is < bs.filterStats.size(); ++is) {
+ tmpFilters.add(bs.filterStats.valueAt(is));
+ }
+ Collections.sort(tmpFilters, comparator);
+ for (FilterStats fs : tmpFilters) {
+ fs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.FILTERS);
+ }
+
+ proto.end(token);
+ }
+ }
+
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
+ IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
+ final long token = proto.start(
+ AlarmManagerServiceProto.ALLOW_WHILE_IDLE_DISPATCHES);
+
+ proto.write(IdleDispatchEntryProto.UID, ent.uid);
+ proto.write(IdleDispatchEntryProto.PKG, ent.pkg);
+ proto.write(IdleDispatchEntryProto.TAG, ent.tag);
+ proto.write(IdleDispatchEntryProto.OP, ent.op);
+ proto.write(IdleDispatchEntryProto.ENTRY_CREATION_REALTIME,
+ ent.elapsedRealtime);
+ proto.write(IdleDispatchEntryProto.ARG_REALTIME, ent.argRealtime);
+
+ proto.end(token);
+ }
+ }
+
+ if (WAKEUP_STATS) {
+ for (WakeupEvent event : mRecentWakeups) {
+ final long token = proto.start(AlarmManagerServiceProto.RECENT_WAKEUP_HISTORY);
+ proto.write(WakeupEventProto.UID, event.uid);
+ proto.write(WakeupEventProto.ACTION, event.action);
+ proto.write(WakeupEventProto.WHEN, event.when);
+ proto.end(token);
+ }
+ }
+ }
+
+ proto.flush();
+ }
+
private void logBatchesLocked(SimpleDateFormat sdf) {
ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
PrintWriter pw = new PrintWriter(bs);
@@ -2328,24 +2655,24 @@ class AlarmManagerService extends SystemService {
alarmSeconds = when / 1000;
alarmNanoseconds = (when % 1000) * 1000 * 1000;
}
-
+
set(mNativeData, type, alarmSeconds, alarmNanoseconds);
} else {
Message msg = Message.obtain();
msg.what = ALARM_EVENT;
-
+
mHandler.removeMessages(ALARM_EVENT);
mHandler.sendMessageAtTime(msg, when);
}
}
private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
- String prefix, String label, long nowRTC, long nowELAPSED, SimpleDateFormat sdf) {
+ String prefix, String label, long nowELAPSED, long nowRTC, SimpleDateFormat sdf) {
for (int i=list.size()-1; i>=0; i--) {
Alarm a = list.get(i);
pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
pw.print(": "); pw.println(a);
- a.dump(pw, prefix + " ", nowRTC, nowELAPSED, sdf);
+ a.dump(pw, prefix + " ", nowELAPSED, nowRTC, sdf);
}
}
@@ -2355,8 +2682,6 @@ class AlarmManagerService extends SystemService {
case RTC_WAKEUP : return "RTC_WAKEUP";
case ELAPSED_REALTIME : return "ELAPSED";
case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP";
- default:
- break;
}
return "--unknown--";
}
@@ -2368,7 +2693,7 @@ class AlarmManagerService extends SystemService {
final String label = labelForType(a.type);
pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
pw.print(": "); pw.println(a);
- a.dump(pw, prefix + " ", nowRTC, nowELAPSED, sdf);
+ a.dump(pw, prefix + " ", nowELAPSED, nowRTC, sdf);
}
}
@@ -2533,7 +2858,7 @@ class AlarmManagerService extends SystemService {
return 0;
}
}
-
+
private static class Alarm {
public final int type;
public final long origWhen;
@@ -2627,7 +2952,7 @@ class AlarmManagerService extends SystemService {
return sb.toString();
}
- public void dump(PrintWriter pw, String prefix, long nowRTC, long nowELAPSED,
+ public void dump(PrintWriter pw, String prefix, long nowELAPSED, long nowRTC,
SimpleDateFormat sdf) {
final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
pw.print(prefix); pw.print("tag="); pw.println(statsTag);
@@ -2656,6 +2981,30 @@ class AlarmManagerService extends SystemService {
pw.print(prefix); pw.print("listener="); pw.println(listener.asBinder());
}
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId, long nowElapsed,
+ long nowRTC) {
+ final long token = proto.start(fieldId);
+
+ proto.write(AlarmProto.TAG, statsTag);
+ proto.write(AlarmProto.TYPE, type);
+ proto.write(AlarmProto.WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
+ proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
+ proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
+ proto.write(AlarmProto.COUNT, count);
+ proto.write(AlarmProto.FLAGS, flags);
+ if (alarmClock != null) {
+ alarmClock.writeToProto(proto, AlarmProto.ALARM_CLOCK);
+ }
+ if (operation != null) {
+ operation.writeToProto(proto, AlarmProto.OPERATION);
+ }
+ if (listener != null) {
+ proto.write(AlarmProto.LISTENER, listener.asBinder().toString());
+ }
+
+ proto.end(token);
+ }
}
void recordWakeupAlarms(ArrayList<Batch> batches, long nowELAPSED, long nowRTC) {
@@ -2752,7 +3101,7 @@ class AlarmManagerService extends SystemService {
{
super("AlarmManager");
}
-
+
public void run()
{
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
@@ -2918,10 +3267,10 @@ class AlarmManagerService extends SystemService {
public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
public static final int LISTENER_TIMEOUT = 3;
public static final int REPORT_ALARMS_ACTIVE = 4;
-
+
public AlarmHandler() {
}
-
+
public void handleMessage(Message msg) {
switch (msg.what) {
case ALARM_EVENT: {
@@ -2969,7 +3318,7 @@ class AlarmManagerService extends SystemService {
}
}
}
-
+
class ClockReceiver extends BroadcastReceiver {
public ClockReceiver() {
IntentFilter filter = new IntentFilter();
@@ -2977,7 +3326,7 @@ class AlarmManagerService extends SystemService {
filter.addAction(Intent.ACTION_DATE_CHANGED);
getContext().registerReceiver(this, filter);
}
-
+
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
@@ -2996,7 +3345,7 @@ class AlarmManagerService extends SystemService {
scheduleDateChangedEvent();
}
}
-
+
public void scheduleTimeTickEvent() {
final long currentTime = System.currentTimeMillis();
final long nextTime = 60000 * ((currentTime / 60000) + 1);
@@ -3026,7 +3375,7 @@ class AlarmManagerService extends SystemService {
Process.myUid(), "android");
}
}
-
+
class InteractiveStateReceiver extends BroadcastReceiver {
public InteractiveStateReceiver() {
IntentFilter filter = new IntentFilter();
@@ -3059,7 +3408,7 @@ class AlarmManagerService extends SystemService {
sdFilter.addAction(Intent.ACTION_UID_REMOVED);
getContext().registerReceiver(this, sdFilter);
}
-
+
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 46b671bd1419..b824fab7a835 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -43,6 +43,7 @@ import android.hardware.health.V2_0.IHealthInfoCallback;
import android.hardware.health.V2_0.IHealth;
import android.hardware.health.V2_0.Result;
import android.os.BatteryManager;
+import android.os.BatteryManagerProto;
import android.os.BatteryManagerInternal;
import android.os.BatteryProperty;
import android.os.Binder;
@@ -913,13 +914,13 @@ public final class BatteryService extends SystemService {
synchronized (mLock) {
proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
- int batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_NONE;
+ int batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_NONE;
if (mHealthInfo.legacy.chargerAcOnline) {
- batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_AC;
+ batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_AC;
} else if (mHealthInfo.legacy.chargerUsbOnline) {
- batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_USB;
+ batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_USB;
} else if (mHealthInfo.legacy.chargerWirelessOnline) {
- batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_WIRELESS;
+ batteryPluggedValue = BatteryManagerProto.PLUG_TYPE_WIRELESS;
}
proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
@@ -1060,6 +1061,7 @@ public final class BatteryService extends SystemService {
}
public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
IHealth service = mHealthServiceWrapper.getLastService();
+ if (service == null) throw new RemoteException("no health service");
final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
switch(id) {
case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
@@ -1101,8 +1103,10 @@ public final class BatteryService extends SystemService {
}
return outResult.value;
}
- public void scheduleUpdate() {
- Slog.e(TAG, "health: must not call scheduleUpdate on battery properties");
+ public void scheduleUpdate() throws RemoteException {
+ IHealth service = mHealthServiceWrapper.getLastService();
+ if (service == null) throw new RemoteException("no health service");
+ service.update();
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 348c79979685..d819a33d9207 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -70,7 +70,6 @@ import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
-import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.util.MultinetworkPolicyTracker;
@@ -127,6 +126,7 @@ import com.android.internal.util.WakeupMessage;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
import com.android.server.connectivity.MockableSystemProperties;
@@ -2265,7 +2265,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Let rematchAllNetworksAndRequests() below record a new default network event
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
- logDefaultNetworkEvent(null, nai);
+ metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(null, nai);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -4995,7 +4995,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Notify system services that this network is up.
makeDefault(newNetwork);
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
- logDefaultNetworkEvent(newNetwork, oldDefaultNetwork);
+ metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+ newNetwork, oldDefaultNetwork);
// Have a new default network, release the transition wakelock in
scheduleReleaseNetworkTransitionWakelock();
}
@@ -5554,25 +5555,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
return ServiceManager.checkService(name) != null;
}
- private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
- int newNetid = NETID_UNSET;
- int prevNetid = NETID_UNSET;
- int[] transports = new int[0];
- boolean hadIPv4 = false;
- boolean hadIPv6 = false;
-
- if (newNai != null) {
- newNetid = newNai.network.netId;
- transports = newNai.networkCapabilities.getTransportTypes();
- }
- if (prevNai != null) {
- prevNetid = prevNai.network.netId;
- final LinkProperties lp = prevNai.linkProperties;
- hadIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
- hadIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
- }
-
- mMetricsLog.log(new DefaultNetworkEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6));
+ @VisibleForTesting
+ protected IpConnectivityMetrics.Logger metricsLogger() {
+ return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+ "no IpConnectivityMetrics service");
}
private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index cf1d33c86173..a139ac4f3f68 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -754,7 +754,7 @@ public class IpSecService extends IIpSecService.Stub {
* and re-binding, during which the system could *technically* hand that port out to someone
* else.
*/
- private void bindToRandomPort(FileDescriptor sockFd) throws IOException {
+ private int bindToRandomPort(FileDescriptor sockFd) throws IOException {
for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) {
try {
FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
@@ -763,7 +763,7 @@ public class IpSecService extends IIpSecService.Stub {
Os.close(probeSocket);
Log.v(TAG, "Binding to port " + port);
Os.bind(sockFd, INADDR_ANY, port);
- return;
+ return port;
} catch (ErrnoException e) {
// Someone miraculously claimed the port just after we closed probeSocket.
if (e.errno == OsConstants.EADDRINUSE) {
@@ -803,7 +803,7 @@ public class IpSecService extends IIpSecService.Stub {
Log.v(TAG, "Binding to port " + port);
Os.bind(sockFd, INADDR_ANY, port);
} else {
- bindToRandomPort(sockFd);
+ port = bindToRandomPort(sockFd);
}
// This code is common to both the unspecified and specified port cases
Os.setsockoptInt(
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cd256109e1bd..bfbce408cb28 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -641,8 +641,8 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
break;
}
case H_PARTITION_FORGET: {
- final String partGuid = (String) msg.obj;
- forgetPartition(partGuid);
+ final VolumeRecord rec = (VolumeRecord) msg.obj;
+ forgetPartition(rec.partGuid, rec.fsUuid);
break;
}
case H_RESET: {
@@ -1694,7 +1694,7 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
synchronized (mLock) {
final VolumeRecord rec = mRecords.remove(fsUuid);
if (rec != null && !TextUtils.isEmpty(rec.partGuid)) {
- mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
+ mHandler.obtainMessage(H_PARTITION_FORGET, rec).sendToTarget();
}
mCallbacks.notifyVolumeForgotten(fsUuid);
@@ -1718,7 +1718,7 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
final String fsUuid = mRecords.keyAt(i);
final VolumeRecord rec = mRecords.valueAt(i);
if (!TextUtils.isEmpty(rec.partGuid)) {
- mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget();
+ mHandler.obtainMessage(H_PARTITION_FORGET, rec).sendToTarget();
}
mCallbacks.notifyVolumeForgotten(fsUuid);
}
@@ -1733,9 +1733,9 @@ class StorageManagerService extends IStorageManager.Stub implements Watchdog.Mon
}
}
- private void forgetPartition(String partGuid) {
+ private void forgetPartition(String partGuid, String fsUuid) {
try {
- mVold.forgetPartition(partGuid);
+ mVold.forgetPartition(partGuid, fsUuid);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index aa0231f2cbff..c04ddf8ae6de 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -38,7 +38,9 @@ import static com.android.server.am.proto.ActivityDisplayProto.CONFIGURATION_CON
import static com.android.server.am.proto.ActivityDisplayProto.STACKS;
import static com.android.server.am.proto.ActivityDisplayProto.ID;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.util.IntArray;
import android.util.Slog;
@@ -206,6 +208,18 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
/**
+ * Returns an existing stack compatible with the input params or creates one
+ * if a compatible stack doesn't exist.
+ * @see #getOrCreateStack(int, int, boolean)
+ */
+ <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, int activityType,
+ boolean onTop) {
+ final int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
+ return getOrCreateStack(windowingMode, activityType, onTop);
+ }
+
+ /**
* Creates a stack matching the input windowing mode and activity type on this display.
* @param windowingMode The windowing mode the stack should be created in. If
* {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
@@ -235,7 +249,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
final ActivityManagerService service = mSupervisor.mService;
- if (!mSupervisor.isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
+ if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
service.mSupportsPictureInPicture, activityType)) {
throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
@@ -252,35 +266,29 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
}
- windowingMode = updateWindowingModeForSplitScreenIfNeeded(windowingMode, activityType);
-
final int stackId = mSupervisor.getNextStackId();
+ return createStackUnchecked(windowingMode, activityType, stackId, onTop);
+ }
- final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop);
+ @VisibleForTesting
+ <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+ }
+ final T stack = (T) new ActivityStack(
+ this, stackId, mSupervisor, windowingMode, activityType, onTop);
if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // Make sure recents stack exist when creating a dock stack as it normally need to be on
- // the other side of the docked stack and we make visibility decisions based on that.
+ // Make sure recents stack exist when creating a dock stack as it normally needs to be
+ // on the other side of the docked stack and we make visibility decisions based on that.
// TODO: Not sure if this is needed after we change to calculate visibility based on
// stack z-order vs. id.
getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop);
}
-
return stack;
}
- @VisibleForTesting
- <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
- int stackId, boolean onTop) {
- switch (windowingMode) {
- case WINDOWING_MODE_PINNED:
- return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
- default:
- return (T) new ActivityStack(
- this, stackId, mSupervisor, windowingMode, activityType, onTop);
- }
- }
-
/**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
@@ -372,6 +380,105 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
}
+ /**
+ * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
+ * @param windowingMode The windowing mode we are checking support for.
+ * @param supportsMultiWindow If we should consider support for multi-window mode in general.
+ * @param supportsSplitScreen If we should consider support for split-screen multi-window.
+ * @param supportsFreeform If we should consider support for freeform multi-window.
+ * @param supportsPip If we should consider support for picture-in-picture mutli-window.
+ * @param activityType The activity type under consideration.
+ * @return true if the windowing mode is supported.
+ */
+ private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+ boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
+ int activityType) {
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED
+ || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ return true;
+ }
+ if (!supportsMultiWindow) {
+ return false;
+ }
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
+ windowingMode, activityType);
+ }
+
+ if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+
+ if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
+ return false;
+ }
+ return true;
+ }
+
+ int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ @Nullable TaskRecord task, int activityType) {
+
+ // First preference if the windowing mode in the activity options if set.
+ int windowingMode = (options != null)
+ ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+ // If windowing mode is unset, then next preference is the candidate task, then the
+ // activity record.
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ if (task != null) {
+ windowingMode = task.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
+ windowingMode = r.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ // Use the display's windowing mode.
+ windowingMode = getWindowingMode();
+ }
+ }
+
+ // Make sure the windowing mode we are trying to use makes sense for what is supported.
+ final ActivityManagerService service = mSupervisor.mService;
+ boolean supportsMultiWindow = service.mSupportsMultiWindow;
+ boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
+ boolean supportsPip = service.mSupportsPictureInPicture;
+ if (supportsMultiWindow) {
+ if (task != null) {
+ supportsMultiWindow = task.isResizeable();
+ supportsSplitScreen = task.supportsSplitScreenWindowingMode();
+ // TODO: Do we need to check for freeform and Pip support here?
+ } else if (r != null) {
+ supportsMultiWindow = r.isResizeable();
+ supportsSplitScreen = r.supportsSplitScreenWindowingMode();
+ supportsFreeform = r.supportsFreeform();
+ supportsPip = r.supportsPictureInPicture();
+ }
+ }
+
+ final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
+ if (!inSplitScreenMode
+ && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
+ // Switch to fullscreen windowing mode if we are not in split-screen mode and we are
+ // trying to launch in split-screen secondary.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ && supportsSplitScreen) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ if (windowingMode != WINDOWING_MODE_UNDEFINED
+ && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+ supportsFreeform, supportsPip, activityType)) {
+ return windowingMode;
+ }
+ // Return the display's windowing mode
+ return getWindowingMode();
+ }
+
/** Returns the top visible stack activity type that isn't in the exclude windowing mode. */
int getTopVisibleStackActivityType(int excludeWindowingMode) {
for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -408,6 +515,14 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
}
}
+ /** We are in the process of exiting split-screen mode. */
+ void onExitingSplitScreenMode() {
+ // Remove reference to the primary-split-screen stack so it no longer has any effect on the
+ // display. For example, we want to be able to create fullscreen stack for standard activity
+ // types when exiting split-screen mode.
+ mSplitScreenPrimaryStack = null;
+ }
+
ActivityStack getSplitScreenPrimaryStack() {
return mSplitScreenPrimaryStack;
}
@@ -424,21 +539,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
return mPinnedStack != null;
}
- int updateWindowingModeForSplitScreenIfNeeded(int windowingMode, int activityType) {
- final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
- if (!inSplitScreenMode
- && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
- // Switch to fullscreen windowing mode if we are not in split-screen mode and we are
- // trying to launch in split-screen secondary.
- return WINDOWING_MODE_FULLSCREEN;
- } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
- && WindowConfiguration.supportSplitScreenWindowingMode(
- windowingMode, activityType)) {
- return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
- return windowingMode;
- }
-
@Override
public String toString() {
return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e1e53b3b0b31..0cb120b00c65 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -31,6 +31,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
@@ -333,6 +334,7 @@ import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.StatsLog;
import android.util.TimingsTraceLog;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
@@ -368,6 +370,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.BinderInternal;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
@@ -2030,7 +2033,9 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
+ // Don't dispatch to isolated processes as they can't access
+ // ConnectivityManager and don't have network privileges anyway.
+ if (r.thread != null && !r.isolated) {
try {
r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
} catch (RemoteException ex) {
@@ -8081,7 +8086,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Adjust the source bounds by the insets for the transition down
final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
- true /* moveHomeStackToFront */, "enterPictureInPictureMode");
+ "enterPictureInPictureMode");
final PinnedActivityStack stack = r.getStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
@@ -10229,11 +10234,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
return;
}
- final ActivityRecord prev = mStackSupervisor.topRunningActivityLocked();
- if (prev != null) {
- task.setTaskToReturnTo(prev);
- }
- mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront",
+ mStackSupervisor.findTaskToMoveToFront(task, flags, options, "moveTaskToFront",
false /* forceNonResizable */);
final ActivityRecord topActivity = task.getTopActivity();
@@ -10318,7 +10319,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// TODO(multi-display): Have the caller pass in the windowing mode and activity type.
final ActivityStack stack = display.createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /*onTop*/);
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+ ON_TOP);
return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
}
}
@@ -12330,6 +12332,9 @@ public class ActivityManagerService extends IActivityManager.Stub
+ android.Manifest.permission.SHUTDOWN);
}
+ // TODO: Where should the corresponding '1' (start) write go?
+ StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, 0);
+
boolean timedout = false;
synchronized(this) {
@@ -13512,6 +13517,7 @@ public class ActivityManagerService extends IActivityManager.Stub
stats.getPackageStatsLocked(sourceUid >= 0 ? sourceUid : uid,
sourcePkg != null ? sourcePkg : rec.key.packageName);
pkg.noteWakeupAlarmLocked(tag);
+ StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid >= 0 ? sourceUid : uid);
}
}
}
@@ -14083,6 +14089,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
+
+ BinderInternal.nSetBinderProxyCountEnabled(true);
+ BinderInternal.setBinderProxyCountCallback(
+ new BinderInternal.BinderProxyLimitListener() {
+ @Override
+ public void onLimitReached(int uid) {
+ Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ + Process.myUid());
+ if (uid == Process.SYSTEM_UID) {
+ Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
+ } else {
+ killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
+ "Too many Binders sent to SYSTEM");
+ }
+ }
+ }, mHandler);
+
traceLog.traceEnd(); // ActivityManagerStartApps
traceLog.traceEnd(); // PhaseActivityManagerReady
}
@@ -14239,12 +14262,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
sb.append("\n");
- if (info.crashInfo != null && info.crashInfo.stackTrace != null) {
- sb.append(info.crashInfo.stackTrace);
+ if (info.hasStackTrace()) {
+ sb.append(info.getStackTrace());
sb.append("\n");
}
- if (info.message != null) {
- sb.append(info.message);
+ if (info.getViolationDetails() != null) {
+ sb.append(info.getViolationDetails());
sb.append("\n");
}
@@ -14900,6 +14923,19 @@ public class ActivityManagerService extends IActivityManager.Stub
mRecentTasks.dump(pw, true /* dumpAll */, dumpPackage);
}
}
+ } else if ("binder-proxies".equals(cmd)) {
+ if (opti >= args.length) {
+ dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(),
+ "Counts of Binder Proxies held by SYSTEM");
+ } else {
+ String uid = args[opti];
+ opti++;
+ // Ensure Binder Proxy Count is as up to date as possible
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
+ }
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
String[] newArgs;
String name;
@@ -15418,6 +15454,34 @@ public class ActivityManagerService extends IActivityManager.Stub
return printed;
}
+ boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) {
+ if(counts != null) {
+ pw.println(header);
+ for (int i = 0; i < counts.size(); i++) {
+ final int uid = counts.keyAt(i);
+ final int binderCount = counts.valueAt(i);
+ pw.print(" UID ");
+ pw.print(uid);
+ pw.print(", binder count = ");
+ pw.print(binderCount);
+ pw.print(", package(s)= ");
+ final String[] pkgNames = mContext.getPackageManager().getPackagesForUid(uid);
+ if (pkgNames != null) {
+ for (int j = 0; j < pkgNames.length; j++) {
+ pw.print(pkgNames[j]);
+ pw.print("; ");
+ }
+ } else {
+ pw.print("NO PACKAGE NAME FOUND");
+ }
+ pw.println();
+ }
+ pw.println();
+ return true;
+ }
+ return false;
+ }
+
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -15623,7 +15687,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
pw.println(" mPreviousProcess: " + mPreviousProcess);
}
- if (dumpAll) {
+ if (dumpAll && (mPreviousProcess == null || dumpPackage == null
+ || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
StringBuilder sb = new StringBuilder(128);
sb.append(" mPreviousProcessVisibleTime: ");
TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
@@ -15642,7 +15707,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mStackSupervisor.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
- pw.println(" mConfigWillChange: " + getFocusedStack().mConfigWillChange);
+ if (dumpPackage == null) {
+ pw.println(" mConfigWillChange: " + getFocusedStack().mConfigWillChange);
+ }
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
for (Map.Entry<String, Integer> entry
@@ -15722,8 +15789,8 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println(" mRunningVoice=" + mRunningVoice);
pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
}
+ pw.println(" mVrController=" + mVrController);
}
- pw.println(" mVrController=" + mVrController);
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
if (dumpPackage == null || dumpPackage.equals(mDebugApp)
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index f03d2d5352b7..4cf2794c3584 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -222,6 +222,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runSetInactive(pw);
case "get-inactive":
return runGetInactive(pw);
+ case "set-standby-bucket":
+ return runSetStandbyBucket(pw);
case "send-trim-memory":
return runSendTrimMemory(pw);
case "display":
@@ -1824,6 +1826,27 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ int runSetStandbyBucket(PrintWriter pw) throws RemoteException {
+ int userId = UserHandle.USER_CURRENT;
+
+ String opt;
+ while ((opt=getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ String packageName = getNextArgRequired();
+ String value = getNextArgRequired();
+
+ IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+ Context.USAGE_STATS_SERVICE));
+ usm.setAppStandbyBucket(packageName, Integer.parseInt(value), userId);
+ return 0;
+ }
+
int runGetInactive(PrintWriter pw) throws RemoteException {
int userId = UserHandle.USER_CURRENT;
@@ -2571,6 +2594,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Sets the inactive state of an app.");
pw.println(" get-inactive [--user <USER_ID>] <PACKAGE>");
pw.println(" Returns the inactive state of an app.");
+ pw.println(" set-standby-bucket [--user <USER_ID>] <PACKAGE> <BUCKET>");
+ pw.println(" Puts an app in the standby bucket.");
pw.println(" send-trim-memory [--user <USER_ID>] <PROCESS>");
pw.println(" [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]");
pw.println(" Send a memory trim event to a <PROCESS>. May also supply a raw trim int level.");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5dc6adc1a817..0e8fc2ce2b54 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -165,6 +165,7 @@ import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.WindowManager.LayoutParams;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
@@ -232,7 +233,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
final boolean stateNotNeeded; // As per ActivityInfo.flags
- boolean fullscreen; // covers the full screen?
+ boolean fullscreen; // The activity is opaque and fills the entire space of this task.
+ // TODO: See if it possible to combine this with the fullscreen field.
+ final boolean hasWallpaper; // Has a wallpaper window as a background.
final boolean noDisplay; // activity is not displayed?
private final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
@@ -274,6 +277,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
PersistableBundle persistentState; // last persistently saved activity state
+ // TODO: See if this is still needed.
boolean frontOfTask; // is this the root activity of its task?
boolean launchFailed; // set if a launched failed, to abort on 2nd try
boolean haveState; // have we gotten the last activity state?
@@ -883,9 +887,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
Entry ent = AttributeCache.instance().get(packageName,
realTheme, com.android.internal.R.styleable.Window, userId);
- fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array);
- noDisplay = ent != null && ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowNoDisplay, false);
+ if (ent != null) {
+ fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
+ hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
+ noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+ } else {
+ hasWallpaper = false;
+ noDisplay = false;
+ }
setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a16105a504b8..7c0df048408d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,13 +16,10 @@
package com.android.server.am;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -107,7 +104,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -346,7 +342,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
private final Rect mTmpRect2 = new Rect();
- private final Point mTmpSize = new Point();
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
@@ -489,26 +484,23 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
activityType = ACTIVITY_TYPE_STANDARD;
}
final ActivityDisplay display = getDisplay();
- if (display != null) {
- if (activityType == ACTIVITY_TYPE_STANDARD
+ if (display != null && activityType == ACTIVITY_TYPE_STANDARD
&& windowingMode == WINDOWING_MODE_UNDEFINED) {
- // Standard activity types will mostly take on the windowing mode of the display if
- // one isn't specified, so look-up a compatible stack based on the display's
- // windowing mode.
- windowingMode = display.getWindowingMode();
- }
- windowingMode =
- display.updateWindowingModeForSplitScreenIfNeeded(windowingMode, activityType);
+ // Standard activity types will mostly take on the windowing mode of the display if one
+ // isn't specified, so look-up a compatible stack based on the display's windowing mode.
+ windowingMode = display.getWindowingMode();
}
return super.isCompatible(windowingMode, activityType);
}
/** Adds the stack to specified display and calls WindowManager to do the same. */
void reparent(ActivityDisplay activityDisplay, boolean onTop) {
+ // TODO: We should probably resolve the windowing mode for the stack on the new display here
+ // so that it end up in a compatible mode in the new display. e.g. split-screen secondary.
removeFromDisplay();
mTmpRect2.setEmpty();
postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
- adjustFocusToNextFocusableStackLocked("reparent", true /* allowFocusSelf */);
+ adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
mStackSupervisor.resumeFocusedStackTopActivityLocked();
// Update visibility of activities before notifying WM. This way it won't try to resize
// windows that are no longer visible.
@@ -837,6 +829,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return mDisplayId == DEFAULT_DISPLAY;
}
+ private boolean returnsToHomeStack() {
+ return !inMultiWindowMode()
+ && !mTaskHistory.isEmpty()
+ && mTaskHistory.get(0).returnsToHomeStack();
+ }
+
void moveToFront(String reason) {
moveToFront(reason, null);
}
@@ -850,6 +848,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return;
}
+ if (!isActivityTypeHome() && returnsToHomeStack()) {
+ // Make sure the home stack is behind this stack since that is where we should return to
+ // when this stack is no longer visible.
+ mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
+ }
+
getDisplay().positionChildAtTop(this);
mStackSupervisor.setFocusStackUnchecked(reason, this);
if (task != null) {
@@ -1496,25 +1500,17 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- /** Returns true if the stack contains a fullscreen task. */
- private boolean hasFullscreenTask() {
- for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
- final TaskRecord task = mTaskHistory.get(i);
- if (task.mFullscreen) {
- return true;
- }
- }
- return false;
- }
-
/**
* Returns true if the stack is translucent and can have other contents visible behind it if
* needed. A stack is considered translucent if it don't contain a visible or
* starting (about to be visible) activity that is fullscreen (opaque).
* @param starting The currently starting activity or null if there is none.
- * @param stackBehind The stack directly behind this one.
*/
- private boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
+ @VisibleForTesting
+ boolean isStackTranslucent(ActivityRecord starting) {
+ if (!isAttached() || mForceHidden) {
+ return true;
+ }
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1533,21 +1529,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
continue;
}
- if (r.fullscreen) {
+ if (r.fullscreen || r.hasWallpaper) {
// Stack isn't translucent if it has at least one fullscreen activity
// that is visible.
return false;
}
-
- final boolean stackBehindHomeOrRecent = stackBehind != null
- && stackBehind.isHomeOrRecentsStack();
- if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack()
- && !stackBehindHomeOrRecent && !isActivityTypeAssistant()) {
- // Stack isn't translucent if it's top activity should have the home stack
- // behind it and the stack currently behind it isn't the home or recents stack
- // or the assistant stack.
- return false;
- }
}
}
return true;
@@ -1572,129 +1558,68 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (!isAttached() || mForceHidden) {
return false;
}
-
- final ActivityDisplay display = getDisplay();
- if (isTopStackOnDisplay() || mStackSupervisor.isFocusedStack(this)) {
- return true;
- }
-
- final int stackIndex = display.getIndexOf(this);
-
- // Check position and visibility of this stack relative to the front stack on its display.
- final ActivityStack topStack = getDisplay().getTopStack();
- final int windowingMode = getWindowingMode();
- final int activityType = getActivityType();
-
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // If the assistant stack is focused and translucent, then the docked stack is always
- // visible
- if (topStack.isActivityTypeAssistant()) {
- return topStack.isStackTranslucent(starting, this);
- }
+ if (mStackSupervisor.isFocusedStack(this)) {
return true;
}
- // Set home stack to invisible when it is below but not immediately below the docked stack
- // A case would be if recents stack exists but has no tasks and is below the docked stack
- // and home stack is below recents
- if (activityType == ACTIVITY_TYPE_HOME) {
- final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
- int dockedStackIndex = display.getIndexOf(splitScreenStack);
- if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) {
- return false;
- }
- }
-
- // Find the first stack behind front stack that actually got something visible.
- int stackBehindTopIndex = display.getIndexOf(topStack) - 1;
- while (stackBehindTopIndex >= 0 &&
- display.getChildAt(stackBehindTopIndex).topRunningActivityLocked() == null) {
- stackBehindTopIndex--;
- }
- final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
- ? display.getChildAt(stackBehindTopIndex) : null;
- int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED;
- int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED;
- if (stackBehindTop != null) {
- stackBehindTopWindowingMode = stackBehindTop.getWindowingMode();
- stackBehindTopActivityType = stackBehindTop.getActivityType();
- }
-
- final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
- if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
- if (this == stackBehindTop) {
- // Stacks directly behind the docked or pinned stack are always visible.
- return true;
- } else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
- // Otherwise, this stack can also be visible if it is directly behind a docked stack
- // or translucent assistant stack behind an always-on-top top-most stack
- if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return true;
- } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
- return stackBehindTop.isStackTranslucent(starting, this);
- }
- }
+ final ActivityRecord top = topRunningActivityLocked();
+ if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
+ // Shouldn't be visible if you don't have any running activities, not starting one, and
+ // not the top stack on display.
+ return false;
}
- if (topStack.isBackdropToTranslucentActivity()
- && topStack.isStackTranslucent(starting, stackBehindTop)) {
- // Stacks behind the fullscreen or assistant stack with a translucent activity are
- // always visible so they can act as a backdrop to the translucent activity.
- // For example, dialog activities
- if (stackIndex == stackBehindTopIndex) {
+ final ActivityDisplay display = getDisplay();
+ boolean gotOpaqueSplitScreenPrimary = false;
+ boolean gotOpaqueSplitScreenSecondary = false;
+ final int windowingMode = getWindowingMode();
+ for (int i = display.getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack other = display.getChildAt(i);
+ if (other == this) {
+ // Should be visible if there is no other stack occluding it.
return true;
}
- if (stackBehindTopIndex >= 0) {
- if ((stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || stackBehindTopWindowingMode == WINDOWING_MODE_PINNED)
- && stackIndex == (stackBehindTopIndex - 1)) {
- // The stack behind the docked or pinned stack is also visible so we can have a
- // complete backdrop to the translucent activity when the docked stack is up.
- return true;
- }
- }
- }
-
- if (isOnHomeDisplay()) {
- // Visibility of any stack on default display should have been determined by the
- // conditions above.
- return false;
- }
- final int stackCount = display.getChildCount();
- for (int i = stackIndex + 1; i < stackCount; i++) {
- final ActivityStack stack = display.getChildAt(i);
+ final int otherWindowingMode = other.getWindowingMode();
+ // TODO: Can be removed once we are no longer using returnToType for back functionality
+ final ActivityStack stackBehind = i > 0 ? display.getChildAt(i - 1) : null;
- if (!stack.mFullscreen && !stack.hasFullscreenTask()) {
- continue;
- }
-
- if (!stack.isDynamicStacksVisibleBehindAllowed()) {
- // These stacks can't have any dynamic stacks visible behind them.
+ if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (other.isStackTranslucent(starting)) {
+ // Can be visible behind a translucent fullscreen stack.
+ continue;
+ }
return false;
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && !gotOpaqueSplitScreenPrimary) {
+ gotOpaqueSplitScreenPrimary =
+ !other.isStackTranslucent(starting);
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && gotOpaqueSplitScreenPrimary) {
+ // Can not be visible behind another opaque stack in split-screen-primary mode.
+ return false;
+ }
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && !gotOpaqueSplitScreenSecondary) {
+ gotOpaqueSplitScreenSecondary =
+ !other.isStackTranslucent(starting);
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && gotOpaqueSplitScreenSecondary) {
+ // Can not be visible behind another opaque stack in split-screen-secondary mode.
+ return false;
+ }
}
-
- if (!stack.isStackTranslucent(starting, null /* stackBehind */)) {
+ if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
+ // Can not be visible if we are in split-screen windowing mode and both halves of
+ // the screen are opaque.
return false;
}
}
+ // Well, nothing is stopping you from being visible...
return true;
}
- private boolean isBackdropToTranslucentActivity() {
- if (isActivityTypeAssistant()) {
- return true;
- }
- final int windowingMode = getWindowingMode();
- return windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
-
- private boolean isDynamicStacksVisibleBehindAllowed() {
- return isActivityTypeAssistant() || getWindowingMode() == WINDOWING_MODE_PINNED;
- }
-
final int rankTaskLayers(int baseLayer) {
int layer = 0;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -1713,6 +1638,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
* Make sure that all activities that need to be visible (that is, they
* currently can be seen by the user) actually are.
*/
+ // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
mTopActivityOccludesKeyguard = false;
@@ -1757,7 +1683,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
visibleIgnoringKeyguard, isTop);
if (visibleIgnoringKeyguard) {
behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
- behindFullscreenActivity, task, r);
+ behindFullscreenActivity, r);
}
if (reallyVisible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
@@ -1816,16 +1742,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// show activities in the next application stack behind them vs. another
// task in the home stack like recents.
behindFullscreenActivity = true;
- } else if (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Skipping after task=" + task
- + " returning to non-application type=" + task.getTaskToReturnTo());
- // Once we reach a fullscreen stack task that has a running activity and should
- // return to another stack task, then no other activities behind that one should
- // be visible.
- if (task.topRunningActivityLocked() != null && !task.returnsToStandardTask()) {
- behindFullscreenActivity = true;
- }
}
}
@@ -1861,24 +1777,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
/**
- * Returns true if we try to maintain focus in the current stack when the top activity finishes.
- */
- private boolean keepFocusInStackIfPossible() {
- final int windowingMode = getWindowingMode();
- return windowingMode == WINDOWING_MODE_FREEFORM
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_PINNED;
- }
-
- /**
- * Returns true if the top task in the task is allowed to return home when finished and
- * there are other tasks in the stack.
- */
- boolean allowTopTaskToReturnHome() {
- return !inPinnedWindowingMode();
- }
-
- /**
* @return the top most visible activity that wants to dismiss Keyguard
*/
ActivityRecord getTopDismissingKeyguardActivity() {
@@ -2039,18 +1937,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
- TaskRecord task, ActivityRecord r) {
+ ActivityRecord r) {
if (r.fullscreen) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// At this point, nothing else needs to be shown in this task.
behindFullscreenActivity = true;
- } else if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack()) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
- + " stackInvisible=" + stackInvisible
- + " behindFullscreenActivity=" + behindFullscreenActivity);
- behindFullscreenActivity = true;
}
return behindFullscreenActivity;
}
@@ -2209,7 +2102,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final boolean hasRunningActivity = next != null;
// TODO: Maybe this entire condition can get removed?
- if (hasRunningActivity && getDisplay() == null) {
+ if (hasRunningActivity && !isAttached()) {
return false;
}
@@ -2239,28 +2132,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return false;
}
- final TaskRecord nextTask = next.getTask();
- final TaskRecord prevTask = prev != null ? prev.getTask() : null;
- if (prevTask != null && prevTask.getStack() == this &&
- prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- if (prevTask == nextTask) {
- prevTask.setFrontOfTask();
- } else if (prevTask != topTask()) {
- // This task is going away but it was supposed to return to the home stack.
- // Now the task above it has to return to the home task instead.
- final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
- mTaskHistory.get(taskNdx).setTaskToReturnTo(ACTIVITY_TYPE_HOME);
- } else if (!isOnHomeDisplay()) {
- return false;
- } else if (!isActivityTypeHome()){
- if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityLocked: Launching home next");
- return isOnHomeDisplay() &&
- mStackSupervisor.resumeHomeStackTask(prev, "prevFinished");
- }
- }
-
// If we are sleeping, and there is no resumed activity, and the top
// activity is paused, well that is the state we want.
if (shouldSleepOrShutDownActivities()
@@ -2644,7 +2515,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
ActivityOptions options, String reason) {
- if (adjustFocusToNextFocusableStackLocked(reason)) {
+ if (adjustFocusToNextFocusableStack(reason)) {
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
@@ -2718,7 +2589,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
private void insertTaskAtTop(TaskRecord task, ActivityRecord starting) {
- updateTaskReturnToForTopInsertion(task);
// TODO: Better place to put all the code below...may be addTask...
mTaskHistory.remove(task);
// Now put task at top.
@@ -2729,57 +2599,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
true /* includingParents */);
}
- /**
- * Updates the {@param task}'s return type before it is moved to the top.
- */
- private void updateTaskReturnToForTopInsertion(TaskRecord task) {
- boolean isLastTaskOverHome = false;
- // If the moving task is over the home or assistant stack, transfer its return type to next
- // task so that they return to the same stack
- if (task.isOverHomeStack() || task.isOverAssistantStack()) {
- final TaskRecord nextTask = getNextTask(task);
- if (nextTask != null) {
- nextTask.setTaskToReturnTo(task.getTaskToReturnTo());
- } else {
- isLastTaskOverHome = true;
- }
- }
-
- // If this is not on the default display, then just set the return type to application
- if (!isOnHomeDisplay()) {
- task.setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
- return;
- }
-
- final ActivityStack lastStack = mStackSupervisor.getLastStack();
-
- // If there is no last task, do not set task to return to
- if (lastStack == null) {
- return;
- }
-
- // If the task was launched from the assistant stack, set the return type to assistant
- if (lastStack.isActivityTypeAssistant()) {
- task.setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT);
- return;
- }
-
- // If this is being moved to the top by another activity or being launched from the home
- // activity, set mTaskToReturnTo accordingly.
- final boolean fromHomeOrRecents = lastStack.isHomeOrRecentsStack();
- final TaskRecord topTask = lastStack.topTask();
- if (!isHomeOrRecentsStack() && (fromHomeOrRecents || topTask() != task)) {
- // If it's a last task over home - we default to keep its return to type not to
- // make underlying task focused when this one will be finished.
- int returnToType = isLastTaskOverHome
- ? task.getTaskToReturnTo() : ACTIVITY_TYPE_STANDARD;
- if (fromHomeOrRecents && allowTopTaskToReturnHome()) {
- returnToType = topTask == null ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
- }
- task.setTaskToReturnTo(returnToType);
- }
- }
-
final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
TaskRecord rTask = r.getTask();
@@ -3049,7 +2868,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
mWindowContainerController.positionChildAtBottom(
- targetTask.getWindowContainerController());
+ targetTask.getWindowContainerController(), false /* includingParents */);
replyChainEnd = -1;
} else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
// If the activity should just be removed -- either
@@ -3285,7 +3104,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
/** Returns true if the task is one of the task finishing on-top of the top running task. */
- boolean isATopFinishingTask(TaskRecord task) {
+ private boolean isATopFinishingTask(TaskRecord task) {
for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
final TaskRecord current = mTaskHistory.get(i);
final ActivityRecord r = current.topRunningActivityLocked();
@@ -3300,7 +3119,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return false;
}
- private void adjustFocusedActivityStackLocked(ActivityRecord r, String reason) {
+ private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
if (!mStackSupervisor.isFocusedStack(this) ||
((mResumedActivity != r) && (mResumedActivity != null))) {
return;
@@ -3309,66 +3128,44 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final ActivityRecord next = topRunningActivityLocked();
final String myReason = reason + " adjustFocus";
- if (next != r) {
- if (next != null && keepFocusInStackIfPossible() && isFocusable()) {
- // For freeform, docked, and pinned stacks we always keep the focus within the
- // stack as long as there is a running activity.
- return;
- } else {
- // Task is not guaranteed to be non-null. For example, destroying the
- // {@link ActivityRecord} will disassociate the task from the activity.
- final TaskRecord task = r.getTask();
+ if (next == r) {
+ mStackSupervisor.moveFocusableActivityStackToFrontLocked(
+ mStackSupervisor.topRunningActivityLocked(), myReason);
+ return;
+ }
- if (task == null) {
- throw new IllegalStateException("activity no longer associated with task:" + r);
- }
+ if (next != null && isFocusable()) {
+ // Keep focus in stack if we have a top running activity and are focusable.
+ return;
+ }
- final boolean isAssistantOrOverAssistant =
- task.getStack().isActivityTypeAssistant() || task.isOverAssistantStack();
- if (r.frontOfTask && isATopFinishingTask(task)
- && (task.isOverHomeStack() || isAssistantOrOverAssistant)) {
- // For non-fullscreen or assistant stack, we want to move the focus to the next
- // visible stack to prevent the home screen from moving to the top and obscuring
- // other visible stacks.
- if ((!mFullscreen || isAssistantOrOverAssistant)
- && adjustFocusToNextFocusableStackLocked(myReason)) {
- return;
- }
- // Move the home stack to the top if this stack is fullscreen or there is no
- // other visible stack.
- if (task.isOverHomeStack() &&
- mStackSupervisor.moveHomeStackTaskToTop(myReason)) {
- // Activity focus was already adjusted. Nothing else to do...
- return;
- }
- }
- }
+ // Task is not guaranteed to be non-null. For example, destroying the
+ // {@link ActivityRecord} will disassociate the task from the activity.
+ final TaskRecord task = r.getTask();
+
+ if (task == null) {
+ throw new IllegalStateException("activity no longer associated with task:" + r);
}
- mStackSupervisor.moveFocusableActivityStackToFrontLocked(
- mStackSupervisor.topRunningActivityLocked(), myReason);
+ // Move focus to next focusable stack if possible.
+ if (adjustFocusToNextFocusableStack(myReason)) {
+ return;
+ }
+
+ // Whatever...go home.
+ mStackSupervisor.moveHomeStackTaskToTop(myReason);
}
/** Find next proper focusable stack and make it focused. */
- private boolean adjustFocusToNextFocusableStackLocked(String reason) {
- return adjustFocusToNextFocusableStackLocked(reason, false /* allowFocusSelf */);
+ private boolean adjustFocusToNextFocusableStack(String reason) {
+ return adjustFocusToNextFocusableStack(reason, false /* allowFocusSelf */);
}
/**
* Find next proper focusable stack and make it focused.
* @param allowFocusSelf Is the focus allowed to remain on the same stack.
*/
- private boolean adjustFocusToNextFocusableStackLocked(String reason, boolean allowFocusSelf) {
- if (isActivityTypeAssistant() && bottomTask() != null
- && bottomTask().returnsToHomeTask()) {
- // If the current stack is the assistant stack, then use the return-to type to determine
- // whether to return to the home screen. This is needed to workaround an issue where
- // launching a fullscreen task (and subequently returning from that task) will cause
- // the fullscreen stack to be found as the next focusable stack below, even if the
- // assistant was launched over home.
- return mStackSupervisor.moveHomeStackTaskToTop(reason);
- }
-
+ private boolean adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
final ActivityStack stack = mStackSupervisor.getNextFocusableStackLocked(
allowFocusSelf ? null : this);
final String myReason = reason + " adjustFocusToNextFocusableStack";
@@ -3378,22 +3175,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final ActivityRecord top = stack.topRunningActivityLocked();
- if (stack.isHomeOrRecentsStack() && (top == null || !top.visible)) {
+ if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
// If we will be focusing on the home stack next and its current top activity isn't
- // visible, then use the task return to value to determine the home task to display
- // next.
+ // visible, then use the move the home stack task to top to make the activity visible.
return mStackSupervisor.moveHomeStackTaskToTop(reason);
}
- if (stack.isActivityTypeAssistant() && top != null
- && top.getTask().returnsToHomeTask()) {
- // It is possible for the home stack to not be directly underneath the assistant stack.
- // For example, the assistant may start an activity in the fullscreen stack. Upon
- // returning to the assistant stack, we must ensure that the home stack is underneath
- // when appropriate.
- mStackSupervisor.moveHomeStackTaskToTop("adjustAssistantReturnToHome");
- }
-
stack.moveToFront(myReason);
return true;
}
@@ -3408,7 +3195,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"stop-no-history", false)) {
// If {@link requestFinishActivityLocked} returns {@code true},
- // {@link adjustFocusedActivityStackLocked} would have been already called.
+ // {@link adjustFocusedActivityStack} would have been already called.
r.resumeKeyDispatchingLocked();
return;
}
@@ -3420,7 +3207,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
if (r.app != null && r.app.thread != null) {
- adjustFocusedActivityStackLocked(r, "stopActivity");
+ adjustFocusedActivityStack(r, "stopActivity");
r.resumeKeyDispatchingLocked();
try {
r.stopped = false;
@@ -3659,7 +3446,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
r.pauseKeyDispatchingLocked();
- adjustFocusedActivityStackLocked(r, "finishActivity");
+ adjustFocusedActivityStack(r, "finishActivity");
finishActivityResultsLocked(r, resultCode, resultData);
@@ -3826,7 +3613,21 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- final boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
+ /** @return true if the stack behind this one is a standard activity type. */
+ boolean inFrontOfStandardStack() {
+ final ActivityDisplay display = getDisplay();
+ if (display == null) {
+ return false;
+ }
+ final int index = display.getIndexOf(this);
+ if (index == 0) {
+ return false;
+ }
+ final ActivityStack stackBehind = display.getChildAt(index - 1);
+ return stackBehind.isActivityTypeStandard();
+ }
+
+ boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
// Basic case: for simple app-centric recents, we need to recreate
// the task if the affinity has changed.
if (srec == null || srec.getTask().affinity == null ||
@@ -3838,10 +3639,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// of a document, unless simply finishing it will return them to the the
// correct app behind.
final TaskRecord task = srec.getTask();
- if (srec.frontOfTask && task != null && task.getBaseIntent() != null
- && task.getBaseIntent().isDocument()) {
+ if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) {
// Okay, this activity is at the root of its task. What to do, what to do...
- if (!task.returnsToStandardTask()) {
+ if (!inFrontOfStandardStack()) {
// Finishing won't return to an application, so we need to recreate.
return true;
}
@@ -4041,11 +3841,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
+ " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
}
- if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
- task.isOverHomeStack()) {
- mStackSupervisor.moveHomeStackTaskToTop(reason);
- }
-
// The following block can be executed multiple times if there is more than one overlay.
// {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
// of the task by id and exiting early if not found.
@@ -4567,60 +4362,19 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId);
- boolean prevIsHome = false;
-
- // If true, we should resume the home activity next if the task we are moving to the
- // back is over the home stack. We force to false if the task we are moving to back
- // is the home task and we don't want it resumed after moving to the back.
- final boolean canGoHome = !tr.isActivityTypeHome() && tr.isOverHomeStack();
- if (canGoHome) {
- final TaskRecord nextTask = getNextTask(tr);
- if (nextTask != null) {
- nextTask.setTaskToReturnTo(tr.getTaskToReturnTo());
- } else {
- prevIsHome = true;
- }
- }
-
- boolean requiresMove = mTaskHistory.indexOf(tr) != 0;
- if (requiresMove) {
- mTaskHistory.remove(tr);
- mTaskHistory.add(0, tr);
- updateTaskMovement(tr, false);
+ mTaskHistory.remove(tr);
+ mTaskHistory.add(0, tr);
+ updateTaskMovement(tr, false);
- mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
- mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController());
- }
+ mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
+ mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController(),
+ true /* includingParents */);
if (inPinnedWindowingMode()) {
mStackSupervisor.removeStack(this);
return true;
}
- // Otherwise, there is an assumption that moving a task to the back moves it behind the
- // home activity. We make sure here that some activity in the stack will launch home.
- int numTasks = mTaskHistory.size();
- for (int taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
- final TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.isOverHomeStack()) {
- break;
- }
- if (taskNdx == 1) {
- // Set the last task before tr to go to home.
- task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
- }
- }
-
- final TaskRecord task = mResumedActivity != null ? mResumedActivity.getTask() : null;
- if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) {
- if (!mService.mBooting && !mService.mBooted) {
- // Not ready yet!
- return false;
- }
- tr.setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
- return mStackSupervisor.resumeHomeStackTask(null, "moveTaskToBack");
- }
-
mStackSupervisor.resumeFocusedStackTopActivityLocked();
return true;
}
@@ -5057,14 +4811,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
onActivityRemovedFromStack(record);
}
- final int taskNdx = mTaskHistory.indexOf(task);
- final int topTaskNdx = mTaskHistory.size() - 1;
- if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
- final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
- if (!nextTask.isOverHomeStack() && !nextTask.isOverAssistantStack()) {
- nextTask.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
- }
- }
mTaskHistory.remove(task);
removeActivitiesFromLRUListLocked(task);
updateTaskMovement(task, true);
@@ -5094,7 +4840,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
&& mStackSupervisor.isFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
- if (mFullscreen || !adjustFocusToNextFocusableStackLocked(myReason)) {
+ if (mFullscreen || !adjustFocusToNextFocusableStack(myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
}
}
@@ -5123,24 +4869,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
addTask(task, toTop, "createTaskRecord");
final boolean isLockscreenShown = mService.mStackSupervisor.mKeyguardController
.isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
- if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable()
- && !isLockscreenShown) {
+ if (!mStackSupervisor.getLaunchingBoundsController().layoutTask(task, info.windowLayout)
+ && mBounds != null && task.isResizeable() && !isLockscreenShown) {
task.updateOverrideConfiguration(mBounds);
}
task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
return task;
}
- boolean layoutTaskInStack(TaskRecord task, ActivityInfo.WindowLayout windowLayout) {
- if (!task.inFreeformWindowingMode()) {
- return false;
- }
- mStackSupervisor.getLaunchingTaskPositioner()
- .updateDefaultBounds(task, mTaskHistory, windowLayout);
-
- return true;
- }
-
ArrayList<TaskRecord> getAllTasks() {
return new ArrayList<>(mTaskHistory);
}
@@ -5167,10 +4903,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mTaskHistory.add(position, task);
task.setStack(this);
- if (toTop) {
- updateTaskReturnToForTopInsertion(task);
- }
-
updateTaskMovement(task, toTop);
postAddTask(task, prevStack, schedulePictureInPictureModeChange);
@@ -5263,7 +4995,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
public String toString() {
return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
+ " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()) + ", "
+ + " mode=" + windowingModeToString(getWindowingMode())
+ + " visible=" + shouldBeVisible(null /* starting */) + ", "
+ mTaskHistory.size() + " tasks}";
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0bdcbf9f2c72..5d8636edaa56 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -110,7 +110,6 @@ import android.app.AppOpsManager;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.WaitResult;
-import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -223,13 +222,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// at the top of its container (e.g. stack).
static final boolean ON_TOP = true;
- // Used to indicate that an objects (e.g. task) removal from its container
- // (e.g. stack) is due to it moving to another container.
- static final boolean MOVING = true;
-
- // Force the focus to change to the stack we are moving a task to..
- static final boolean FORCE_FOCUS = true;
-
// Don't execute any calls to resume.
static final boolean DEFER_RESUME = true;
@@ -295,7 +287,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
- LaunchingTaskPositioner mTaskPositioner = new LaunchingTaskPositioner();
+ private final LaunchingBoundsController mLaunchingBoundsController;
/** Counter for next free stack ID to use for dynamic activity stacks. */
private int mNextFreeStackId = 0;
@@ -405,6 +397,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* object each time.
*/
private final Rect tempRect = new Rect();
+ private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
@@ -575,6 +568,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mHandler = new ActivityStackSupervisorHandler(looper);
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
mKeyguardController = new KeyguardController(service, this);
+
+ mLaunchingBoundsController = new LaunchingBoundsController();
+ mLaunchingBoundsController.registerDefaultPositioners(this);
}
void setRecentTasks(RecentTasks recentTasks) {
@@ -685,10 +681,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return false;
}
- if (prev != null) {
- prev.getTask().setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
- }
-
mHomeStack.moveHomeStackTaskToTop();
ActivityRecord r = getHomeActivity();
final String myReason = reason + " resumeHomeStackTask";
@@ -2097,23 +2089,28 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- void findTaskToMoveToFrontLocked(TaskRecord task, int flags, ActivityOptions options,
+ void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options,
String reason, boolean forceNonResizeable) {
- if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
- mUserLeaving = true;
- }
- if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
- // Caller wants the home activity moved with it. To accomplish this,
- // we'll just indicate that this task returns to the home task.
- task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
- }
final ActivityStack currentStack = task.getStack();
if (currentStack == null) {
- Slog.e(TAG, "findTaskToMoveToFrontLocked: can't move task="
+ Slog.e(TAG, "findTaskToMoveToFront: can't move task="
+ task + " to front. Stack is null");
return;
}
+ if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+ mUserLeaving = true;
+ }
+
+ final ActivityRecord prev = topRunningActivityLocked();
+
+ if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0
+ || (prev != null && prev.isActivityTypeRecents())) {
+ // Caller wants the home activity moved with it or the previous task is recents in which
+ // case we always return home from the task we are moving to the front.
+ moveHomeStackToFront("findTaskToMoveToFront");
+ }
+
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
task.updateOverrideConfiguration(bounds);
@@ -2122,7 +2119,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (stack != currentStack) {
task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
- "findTaskToMoveToFrontLocked");
+ "findTaskToMoveToFront");
stack = currentStack;
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
@@ -2161,8 +2158,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|| mService.mSupportsFreeformWindowManagement;
}
- LaunchingTaskPositioner getLaunchingTaskPositioner() {
- return mTaskPositioner;
+ LaunchingBoundsController getLaunchingBoundsController() {
+ return mLaunchingBoundsController;
}
protected <T extends ActivityStack> T getStack(int stackId) {
@@ -2186,89 +2183,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return null;
}
- /**
- * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
- * @param windowingMode The windowing mode we are checking support for.
- * @param supportsMultiWindow If we should consider support for multi-window mode in general.
- * @param supportsSplitScreen If we should consider support for split-screen multi-window.
- * @param supportsFreeform If we should consider support for freeform multi-window.
- * @param supportsPip If we should consider support for picture-in-picture mutli-window.
- * @param activityType The activity type under consideration.
- * @return true if the windowing mode is supported.
- */
- boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
- boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
- int activityType) {
-
- if (windowingMode == WINDOWING_MODE_UNDEFINED
- || windowingMode == WINDOWING_MODE_FULLSCREEN) {
- return true;
- }
- if (!supportsMultiWindow) {
- return false;
- }
-
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
- windowingMode, activityType);
- }
-
- if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
- return false;
- }
-
- if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
- return false;
- }
- return true;
- }
-
- private int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable TaskRecord task, int activityType) {
-
- // First preference if the windowing mode in the activity options if set.
- int windowingMode = (options != null)
- ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
-
- // If windowing mode is unset, then next preference is the candidate task, then the
- // activity record.
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- if (task != null) {
- windowingMode = task.getWindowingMode();
- }
- if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
- windowingMode = r.getWindowingMode();
- }
- }
-
- // Make sure the windowing mode we are trying to use makes sense for what is supported.
- boolean supportsMultiWindow = mService.mSupportsMultiWindow;
- boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
- boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
- boolean supportsPip = mService.mSupportsPictureInPicture;
- if (supportsMultiWindow) {
- if (task != null) {
- supportsMultiWindow = task.isResizeable();
- supportsSplitScreen = task.supportsSplitScreenWindowingMode();
- // TODO: Do we need to check for freeform and Pip support here?
- } else if (r != null) {
- supportsMultiWindow = r.isResizeable();
- supportsSplitScreen = r.supportsSplitScreenWindowingMode();
- supportsFreeform = r.supportsFreeform();
- supportsPip = r.supportsPictureInPicture();
- }
- }
-
- if (windowingMode != WINDOWING_MODE_UNDEFINED
- && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
- supportsFreeform, supportsPip, activityType)) {
- return windowingMode;
- }
- // Return root/systems windowing mode
- return getWindowingMode();
- }
-
int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@Nullable TaskRecord task) {
// Preference is given to the activity type for the activity then the task since the type
@@ -2329,7 +2243,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
final int activityType = resolveActivityType(r, options, candidateTask);
- int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType);
T stack = null;
// Next preference for stack goes to the display Id set in the activity options or the
@@ -2347,7 +2260,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
if (display != null) {
- stack = display.getOrCreateStack(windowingMode, activityType, onTop);
+ stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
if (stack != null) {
return stack;
}
@@ -2365,10 +2278,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
stack = r.getStack();
}
if (stack != null) {
- if (stack.isCompatible(windowingMode, activityType)) {
- return stack;
- }
display = stack.getDisplay();
+ if (display != null) {
+ final int windowingMode =
+ display.resolveWindowingMode(r, options, candidateTask, activityType);
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ }
}
if (display == null
@@ -2379,7 +2296,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
display = getDefaultDisplay();
}
- return display.getOrCreateStack(windowingMode, activityType, onTop);
+ return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
}
/**
@@ -2596,6 +2513,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Tell the display we are exiting split-screen mode.
+ toDisplay.onExitingSplitScreenMode();
// We are moving all tasks from the docked stack to the fullscreen stack,
// which is dismissing the docked stack, so resize all other stacks to
// fullscreen here already so we don't end up with resize trashing.
@@ -2625,35 +2544,27 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
if (!tasks.isEmpty()) {
+ mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
final int size = tasks.size();
- final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
-
- if (onTop) {
- final int returnToType =
- toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
+ for (int i = 0; i < size; ++i) {
+ final TaskRecord task = tasks.get(i);
+ final ActivityStack toStack = toDisplay.getOrCreateStack(
+ null, mTmpOptions, task, task.getActivityType(), onTop);
+
+ if (onTop) {
+ final int returnToType =
+ toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
final boolean isTopTask = i == (size - 1);
- if (inPinnedWindowingMode) {
- // Update the return-to to reflect where the pinned stack task was moved
- // from so that we retain the stack that was previously visible if the
- // pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(returnToType);
- }
// Defer resume until all the tasks have been moved to the fullscreen stack
- task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+ task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
isTopTask /* animate */, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - onTop");
- }
- } else {
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
+ } else {
// Position the tasks in the fullscreen stack in order at the bottom of the
// stack. Also defer resume until all the tasks have been moved to the
// fullscreen stack.
- task.reparent(fullscreenStack, i /* position */,
+ task.reparent(toStack, ON_TOP,
REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
schedulePictureInPictureModeChange,
"moveTasksToFullscreenStack - NOT_onTop");
@@ -3080,23 +2991,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
+ " task=" + task);
}
- // We don't allow moving a unresizeable task to the docked stack since the docked stack is
- // used for split-screen mode and will cause things like the docked divider to show up. We
- // instead leave the task in its current stack or move it to the fullscreen stack if it
- // isn't currently in a stack.
+ // Leave the task in its current stack or a fullscreen stack if it isn't resizeable and the
+ // preferred stack is in multi-window mode.
if (inMultiWindowMode && !task.isResizeable()) {
- Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack."
- + " Moving to stackId=" + stackId + " instead.");
- // Temporarily disable resizeablility of the task as we don't want it to be resized if,
- // for example, a docked stack is created which will lead to the stack we are moving
- // from being resized and and its resizeable tasks being resized.
- try {
- task.mTemporarilyUnresizable = true;
- stack = stack.getDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
- } finally {
- task.mTemporarilyUnresizable = false;
+ Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window stack=" + stack
+ + " Moving to a fullscreen stack instead.");
+ if (prevStack != null) {
+ return prevStack;
}
+ stack = stack.getDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop);
}
return stack;
}
@@ -3123,12 +3027,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
- true /* moveHomeStackToFront */, "moveTopActivityToPinnedStack");
+ "moveTopActivityToPinnedStack");
return true;
}
void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
- boolean moveHomeStackToFront, String reason) {
+ String reason) {
mWindowManager.deferSurfaceLayout();
@@ -3154,17 +3058,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
true /* allowResizeInDockedMode */, !DEFER_RESUME);
if (task.mActivities.size() == 1) {
- // There is only one activity in the task. So, we can just move the task over to
- // the stack without re-parenting the activity in a different task. We don't
- // move the home stack forward if we are currently entering picture-in-picture
- // while pausing because that changes the focused stack and may prevent the new
- // starting activity from resuming.
- if (moveHomeStackToFront && task.returnsToHomeTask()
- && (r.state == RESUMED || !r.supportsEnterPipOnTaskSwitch)) {
- // Move the home stack forward if the task we just moved to the pinned stack
- // was launched from home so home should be visible behind it.
- moveHomeStackToFront(reason);
- }
// Defer resume until below, and do not schedule PiP changes until we animate below
task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
false /* schedulePictureInPictureModeChange */, reason);
@@ -4287,9 +4180,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
final boolean inSplitScreenMode = actualStack != null
- && actualStack.inSplitScreenWindowingMode();
+ && actualStack.getDisplay().hasSplitScreenPrimaryStack();
if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
- && !isSecondaryDisplayPreferred) || task.isActivityTypeHome()) {
+ && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
return;
}
@@ -4606,6 +4499,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
"startActivityFromRecents: Task " + taskId + " not found.");
}
+ // We always want to return to the home activity instead of the recents activity from
+ // whatever is started from the recents activity, so move the home stack forward.
+ moveHomeStackToFront("startActivityFromRecents");
+
// If the user must confirm credentials (e.g. when first launching a work app and the
// Work Challenge is present) let startActivityInPackage handle the intercepting.
if (!mService.mUserController.shouldConfirmCredentials(task.userId)
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 6f74d85115c9..f6905c58d928 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -151,7 +151,7 @@ class ActivityStarter {
private boolean mLaunchTaskBehind;
private int mLaunchFlags;
- private Rect mLaunchBounds;
+ private Rect mLaunchBounds = new Rect();
private ActivityRecord mNotTop;
private boolean mDoResume;
@@ -169,9 +169,6 @@ class ActivityStarter {
private Intent mNewTaskIntent;
private ActivityStack mSourceStack;
private ActivityStack mTargetStack;
- // Indicates that we moved other task and are going to put something on top soon, so
- // we don't want to show it redundantly or accidentally change what's shown below.
- private boolean mMovedOtherTask;
private boolean mMovedToFront;
private boolean mNoAnimation;
private boolean mKeepCurTransition;
@@ -210,7 +207,7 @@ class ActivityStarter {
mLaunchFlags = 0;
mLaunchMode = INVALID_LAUNCH_MODE;
- mLaunchBounds = null;
+ mLaunchBounds.setEmpty();
mNotTop = null;
mDoResume = false;
@@ -227,7 +224,6 @@ class ActivityStarter {
mSourceStack = null;
mTargetStack = null;
- mMovedOtherTask = false;
mMovedToFront = false;
mNoAnimation = false;
mKeepCurTransition = false;
@@ -1184,12 +1180,8 @@ class ActivityStarter {
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
- if (mSourceRecord != null) {
- mStartActivity.getTask().setTaskToReturnTo(mSourceRecord);
- }
if (newTask) {
- EventLog.writeEvent(
- EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
+ EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
mStartActivity.getTask().taskId);
}
ActivityStack.logStartActivity(
@@ -1254,7 +1246,10 @@ class ActivityStarter {
mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options);
- mLaunchBounds = getOverrideBounds(r, options, inTask);
+ mLaunchBounds.setEmpty();
+
+ mSupervisor.getLaunchingBoundsController().calculateBounds(inTask, null /*layout*/, r,
+ options, mLaunchBounds);
mLaunchMode = r.launchMode;
@@ -1579,7 +1574,6 @@ class ActivityStarter {
if (mLaunchTaskBehind && mSourceRecord != null) {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
- mMovedOtherTask = true;
// If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
// will be cleared soon by ActivityStarter in setTaskFromIntentActivity().
@@ -1644,7 +1638,6 @@ class ActivityStarter {
intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
true /* taskSwitch */);
}
- updateTaskReturnToType(intentActivity.getTask(), mLaunchFlags, focusStack);
}
}
if (!mMovedToFront && mDoResume) {
@@ -1663,27 +1656,6 @@ class ActivityStarter {
return intentActivity;
}
- private void updateTaskReturnToType(
- TaskRecord task, int launchFlags, ActivityStack focusedStack) {
- if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
- == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
- // Caller wants to appear on home activity.
- task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
- return;
- } else if (focusedStack == null || focusedStack.isActivityTypeHome()) {
- // Task will be launched over the home stack, so return home.
- task.setTaskToReturnTo(ACTIVITY_TYPE_HOME);
- return;
- } else if (focusedStack != task.getStack() && focusedStack.isActivityTypeAssistant()) {
- // Task was launched over the assistant stack, so return there
- task.setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT);
- return;
- }
-
- // Else we are coming from an application stack so return to an application.
- task.setTaskToReturnTo(ACTIVITY_TYPE_STANDARD);
- }
-
private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
@@ -1700,11 +1672,6 @@ class ActivityStarter {
task.performClearTaskLocked();
mReuseTask = task;
mReuseTask.setIntent(mStartActivity);
-
- // When we clear the task - focus will be adjusted, which will bring another task
- // to top before we launch the activity we need. This will temporary swap their
- // mTaskToReturnTo values and we don't want to overwrite them accidentally.
- mMovedOtherTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
@@ -1725,7 +1692,7 @@ class ActivityStarter {
// Target stack got cleared when we all activities were removed above.
// Go ahead and reset it.
mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
- null /* bounds */, mLaunchFlags, mOptions);
+ mLaunchFlags, mOptions);
mTargetStack.addTask(task,
!mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
}
@@ -1776,8 +1743,7 @@ class ActivityStarter {
private int setTaskFromReuseOrCreateNewTask(
TaskRecord taskToAffiliate, ActivityStack topStack) {
- mTargetStack = computeStackFocus(
- mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions);
+ mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
// Do no move the target stack to front yet, as we might bail if
// isLockTaskModeViolation fails below.
@@ -1806,15 +1772,6 @@ class ActivityStarter {
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- if (!mMovedOtherTask) {
- // If stack id is specified in activity options, usually it means that activity is
- // launched not from currently focused stack (e.g. from SysUI or from shell) - in
- // that case we check the target stack.
- // TODO: Not sure I understand the value or use of the commented out code and the
- // comment above. See if this causes any issues and why...
- updateTaskReturnToType(mStartActivity.getTask(), mLaunchFlags,
- /*preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : */topStack);
- }
if (mDoResume) {
mTargetStack.moveToFront("reuseOrNewTask");
}
@@ -1962,7 +1919,7 @@ class ActivityStarter {
return START_TASK_TO_FRONT;
}
- if (mLaunchBounds != null) {
+ if (!mLaunchBounds.isEmpty()) {
// TODO: Shouldn't we already know what stack to use by the time we get here?
ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
if (stack != mInTask.getStack()) {
@@ -1985,7 +1942,7 @@ class ActivityStarter {
}
void updateBounds(TaskRecord task, Rect bounds) {
- if (bounds == null) {
+ if (bounds.isEmpty()) {
return;
}
@@ -1998,8 +1955,7 @@ class ActivityStarter {
}
private void setTaskToCurrentTopOrCreateNewTask() {
- mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
- mOptions);
+ mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions);
if (mDoResume) {
mTargetStack.moveToFront("addingToTopTask");
}
@@ -2062,8 +2018,8 @@ class ActivityStarter {
}
}
- private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
- int launchFlags, ActivityOptions aOptions) {
+ private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
+ ActivityOptions aOptions) {
final TaskRecord task = r.getTask();
ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
if (stack != null) {
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index d42a3b9bc1f0..38b3039f1856 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import android.app.ActivityManager;
@@ -76,7 +77,8 @@ class AppTaskImpl extends IAppTask.Stub {
synchronized (mService) {
long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
+ TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
@@ -110,7 +112,8 @@ class AppTaskImpl extends IAppTask.Stub {
TaskRecord tr;
IApplicationThread appThread;
synchronized (mService) {
- tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
+ tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
@@ -131,7 +134,8 @@ class AppTaskImpl extends IAppTask.Stub {
synchronized (mService) {
long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId);
+ TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 68ed9aecf31b..db12ae252abc 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -297,31 +297,39 @@ public final class BatteryStatsService extends IBatteryStats.Stub
void noteProcessStart(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessStartLocked(name, uid);
+ // TODO: decide where this should be and use a constant instead of a literal.
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1);
}
}
void noteProcessCrash(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessCrashLocked(name, uid);
+ // TODO: decide where this should be and use a constant instead of a literal.
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2);
}
}
void noteProcessAnr(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessAnrLocked(name, uid);
+ // TODO: decide where this should be and use a constant instead of a literal.
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3);
}
}
void noteProcessFinish(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessFinishLocked(name, uid);
+ // TODO: decide where this should be and use a constant instead of a literal.
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0);
}
}
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
// TODO: remove this once we figure out properly where and how
- StatsLog.write(StatsLog.PROCESS_STATE_CHANGED, uid, state);
+ StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid, state);
mStats.noteUidProcessStateLocked(uid, state);
}
@@ -548,6 +556,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
public void noteScreenBrightness(int brightness) {
enforceCallingPermission();
synchronized (mStats) {
+ StatsLog.write(StatsLog.SCREEN_BRIGHTNESS_CHANGED, brightness);
mStats.noteScreenBrightnessLocked(brightness);
}
}
@@ -909,6 +918,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
enforceCallingPermission();
synchronized (mStats) {
mStats.noteDeviceIdleModeLocked(mode, activeReason, activeUid);
+ StatsLog.write(StatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mode);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c62cc38b716f..aa82d000386a 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1198,11 +1198,6 @@ public final class BroadcastQueue {
+ " (uid " + r.callingUid + ")");
skip = true;
}
- if (!skip) {
- r.manifestCount++;
- } else {
- r.manifestSkipCount++;
- }
if (r.curApp != null && r.curApp.crashing) {
// If the target process is crashing, just skip it.
Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
@@ -1283,6 +1278,16 @@ public final class BroadcastQueue {
}
}
+ if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
+ && !mService.mUserController
+ .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
+ 0 /* flags */)) {
+ skip = true;
+ Slog.w(TAG,
+ "Skipping delivery to " + info.activityInfo.packageName + " / "
+ + info.activityInfo.applicationInfo.uid + " : user is not running");
+ }
+
if (skip) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Skipping delivery of ordered [" + mQueueName + "] "
@@ -1291,9 +1296,11 @@ public final class BroadcastQueue {
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
+ r.manifestSkipCount++;
scheduleBroadcastsLocked();
return;
}
+ r.manifestCount++;
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
@@ -1302,7 +1309,7 @@ public final class BroadcastQueue {
if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
- + info.activityInfo.applicationInfo.uid);
+ + receiverUid);
}
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
@@ -1365,7 +1372,7 @@ public final class BroadcastQueue {
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
+ info.activityInfo.applicationInfo.packageName + "/"
- + info.activityInfo.applicationInfo.uid + " for broadcast "
+ + receiverUid + " for broadcast "
+ r.intent + ": process is bad");
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
diff --git a/services/core/java/com/android/server/am/LaunchingActivityPositioner.java b/services/core/java/com/android/server/am/LaunchingActivityPositioner.java
new file mode 100644
index 000000000000..5815e9809756
--- /dev/null
+++ b/services/core/java/com/android/server/am/LaunchingActivityPositioner.java
@@ -0,0 +1,62 @@
+/*
+ * 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.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner;
+
+/**
+ * An implementation of {@link LaunchingBoundsPositioner}, which applies the launch bounds specified
+ * inside {@link ActivityOptions#getLaunchBounds()}.
+ */
+public class LaunchingActivityPositioner implements LaunchingBoundsPositioner {
+ private final ActivityStackSupervisor mSupervisor;
+
+ LaunchingActivityPositioner(ActivityStackSupervisor activityStackSupervisor) {
+ mSupervisor = activityStackSupervisor;
+ }
+
+ @Override
+ public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
+ ActivityRecord activity, ActivityOptions options, Rect current, Rect result) {
+ // We only care about figuring out bounds for activities.
+ if (activity == null) {
+ return RESULT_SKIP;
+ }
+
+ // Activity must be resizeable in the specified task.
+ if (!(mSupervisor.canUseActivityOptionsLaunchBounds(options)
+ && (activity.isResizeable() || (task != null && task.isResizeable())))) {
+ return RESULT_SKIP;
+ }
+
+ final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
+
+ // Bounds weren't valid.
+ if (bounds == null) {
+ return RESULT_SKIP;
+ }
+
+ result.set(bounds);
+
+ // When this is the most explicit position specification so we should not allow further
+ // modification of the position.
+ return RESULT_DONE;
+ }
+}
diff --git a/services/core/java/com/android/server/am/LaunchingBoundsController.java b/services/core/java/com/android/server/am/LaunchingBoundsController.java
new file mode 100644
index 000000000000..8345ba657c2d
--- /dev/null
+++ b/services/core/java/com/android/server/am/LaunchingBoundsController.java
@@ -0,0 +1,160 @@
+/*
+ * 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.IntDef;
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.graphics.Rect;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
+
+/**
+ * {@link LaunchingBoundsController} calculates the launch bounds by coordinating between registered
+ * {@link LaunchingBoundsPositioner}.
+ */
+class LaunchingBoundsController {
+ private final List<LaunchingBoundsPositioner> mPositioners = new ArrayList<>();
+
+ // Temporary {@link Rect} for calculations. This is kept separate from {@code mTmpCurrent} and
+ // {@code mTmpResult} to prevent clobbering values.
+ private final Rect mTmpRect = new Rect();
+
+ private final Rect mTmpCurrent = new Rect();
+ private final Rect mTmpResult = new Rect();
+
+ /**
+ * Creates a {@link LaunchingBoundsController} with default registered
+ * {@link LaunchingBoundsPositioner}s.
+ */
+ void registerDefaultPositioners(ActivityStackSupervisor supervisor) {
+ // {@link LaunchingTaskPositioner} handles window layout preferences.
+ registerPositioner(new LaunchingTaskPositioner());
+
+ // {@link LaunchingActivityPositioner} is the most specific positioner and thus should be
+ // registered last (applied first) out of the defaults.
+ registerPositioner(new LaunchingActivityPositioner(supervisor));
+ }
+
+ /**
+ * Returns the position calculated by the registered positioners
+ * @param task The {@link TaskRecord} currently being positioned.
+ * @param layout The specified {@link WindowLayout}.
+ * @param activity The {@link ActivityRecord} currently being positioned.
+ * @param options The {@link ActivityOptions} specified for the activity.
+ * @param result The resulting bounds. If no bounds are set, {@link Rect#isEmpty()} will be
+ * true.
+ */
+ void calculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+ ActivityOptions options, Rect result) {
+ result.setEmpty();
+
+ // We start at the last registered {@link LaunchingBoundsPositioner} as this represents
+ // The positioner closest to the product level. Moving back through the list moves closer to
+ // the platform logic.
+ for (int i = mPositioners.size() - 1; i >= 0; --i) {
+ mTmpResult.setEmpty();
+ mTmpCurrent.set(result);
+ final LaunchingBoundsPositioner positioner = mPositioners.get(i);
+
+ switch(positioner.onCalculateBounds(task, layout, activity, options, mTmpCurrent,
+ mTmpResult)) {
+ case RESULT_SKIP:
+ // Do not apply any results when we are told to skip
+ continue;
+ case RESULT_DONE:
+ // Set result and return immediately.
+ result.set(mTmpResult);
+ return;
+ case RESULT_CONTINUE:
+ // Set result and continue
+ result.set(mTmpResult);
+ break;
+ }
+ }
+ }
+
+ /**
+ * A convenience method for laying out a task.
+ * @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 /*options*/, mTmpRect);
+
+ if (mTmpRect.isEmpty()) {
+ return false;
+ }
+
+ task.updateOverrideConfiguration(mTmpRect);
+
+ return true;
+ }
+
+ /**
+ * Adds a positioner to participate in future bounds calculation. Note that the last registered
+ * {@link LaunchingBoundsPositioner} will be the first to calculate the bounds.
+ */
+ void registerPositioner(LaunchingBoundsPositioner positioner) {
+ if (mPositioners.contains(positioner)) {
+ return;
+ }
+
+ mPositioners.add(positioner);
+ }
+
+ /**
+ * An interface implemented by those wanting to participate in bounds calculation.
+ */
+ interface LaunchingBoundsPositioner {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE})
+ @interface Result {}
+
+ // Returned when the positioner does not want to influence the bounds calculation
+ int RESULT_SKIP = 0;
+ // Returned when the positioner has changed the bounds and would like its results to be the
+ // final bounds applied.
+ int RESULT_DONE = 1;
+ // Returned when the positioner has changed the bounds but is okay with other positioners
+ // influencing the bounds.
+ int RESULT_CONTINUE = 2;
+
+ /**
+ * Called when asked to calculate bounds.
+ * @param task The {@link TaskRecord} currently being positioned.
+ * @param layout The specified {@link WindowLayout}.
+ * @param activity The {@link ActivityRecord} currently being positioned.
+ * @param options The {@link ActivityOptions} specified for the activity.
+ * @param current The current bounds. This can differ from the initial bounds as it
+ * represents the modified bounds up to this point.
+ * @param result The {@link Rect} which the positioner should return its modified bounds.
+ * Any merging of the current bounds should be already applied to this
+ * value as well before returning.
+ * @return A {@link Result} representing the result of the bounds calculation.
+ */
+ @Result
+ int onCalculateBounds(TaskRecord task, WindowLayout layout, ActivityRecord activity,
+ ActivityOptions options, Rect current, Rect result);
+ }
+}
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index 0dc73e98f492..6389075a9b3f 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -19,7 +19,7 @@ package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import android.annotation.Nullable;
+import android.app.ActivityOptions;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
@@ -36,8 +36,10 @@ import java.util.ArrayList;
* and compares corners of the task with corners of existing tasks. If some two pairs of corners are
* sufficiently close enough, it shifts the bounds of the new task and tries again. When it exhausts
* all possible shifts, it gives up and puts the task in the original position.
+ *
+ * Note that the only gravities of concern are the corners and the center.
*/
-class LaunchingTaskPositioner {
+class LaunchingTaskPositioner implements LaunchingBoundsController.LaunchingBoundsPositioner {
private static final String TAG = TAG_WITH_CLASS_NAME ? "LaunchingTaskPositioner" : TAG_AM;
// Determines how close window frames/corners have to be to call them colliding.
@@ -74,44 +76,50 @@ class LaunchingTaskPositioner {
* Tries to set task's bound in a way that it won't collide with any other task. By colliding
* we mean that two tasks have left-top corner very close to each other, so one might get
* obfuscated by the other one.
- *
- * @param task Task for which we want to find bounds that won't collide with other.
- * @param tasks Existing tasks with which we don't want to collide.
- * @param windowLayout Optional information from the client about how it would like to be sized
- * and positioned.
*/
- void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks,
- @Nullable ActivityInfo.WindowLayout windowLayout) {
+ @Override
+ public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
+ ActivityRecord activity, ActivityOptions options, Rect current, Rect result) {
+ // We can only apply positioning if we're in a freeform stack.
+ if (task == null || task.getStack() == null || !task.inFreeformWindowingMode()) {
+ return RESULT_SKIP;
+ }
+
+ final ArrayList<TaskRecord> tasks = task.getStack().getAllTasks();
+
updateAvailableRect(task, mAvailableRect);
- if (windowLayout == null) {
- positionCenter(task, tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
- getFreeformHeight(mAvailableRect));
- return;
+ if (layout == null) {
+ positionCenter(tasks, mAvailableRect, getFreeformWidth(mAvailableRect),
+ getFreeformHeight(mAvailableRect), result);
+ return RESULT_CONTINUE;
}
- int width = getFinalWidth(windowLayout, mAvailableRect);
- int height = getFinalHeight(windowLayout, mAvailableRect);
- int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+
+ int width = getFinalWidth(layout, mAvailableRect);
+ int height = getFinalHeight(layout, mAvailableRect);
+ int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
if (verticalGravity == Gravity.TOP) {
if (horizontalGravity == Gravity.RIGHT) {
- positionTopRight(task, tasks, mAvailableRect, width, height);
+ positionTopRight(tasks, mAvailableRect, width, height, result);
} else {
- positionTopLeft(task, tasks, mAvailableRect, width, height);
+ positionTopLeft(tasks, mAvailableRect, width, height, result);
}
} else if (verticalGravity == Gravity.BOTTOM) {
if (horizontalGravity == Gravity.RIGHT) {
- positionBottomRight(task, tasks, mAvailableRect, width, height);
+ positionBottomRight(tasks, mAvailableRect, width, height, result);
} else {
- positionBottomLeft(task, tasks, mAvailableRect, width, height);
+ positionBottomLeft(tasks, mAvailableRect, width, height, result);
}
} else {
// Some fancy gravity setting that we don't support yet. We just put the activity in the
// center.
- Slog.w(TAG, "Received unsupported gravity: " + windowLayout.gravity
+ Slog.w(TAG, "Received unsupported gravity: " + layout.gravity
+ ", positioning in the center instead.");
- positionCenter(task, tasks, mAvailableRect, width, height);
+ positionCenter(tasks, mAvailableRect, width, height, result);
}
+
+ return RESULT_CONTINUE;
}
private void updateAvailableRect(TaskRecord task, Rect availableRect) {
@@ -179,50 +187,50 @@ class LaunchingTaskPositioner {
return height;
}
- private void positionBottomLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
+ private void positionBottomLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
+ int height, Rect result) {
mTmpProposal.set(availableRect.left, availableRect.bottom - height,
availableRect.left + width, availableRect.bottom);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_RIGHT);
+ position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT,
+ result);
}
- private void positionBottomRight(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
+ private void positionBottomRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
+ int height, Rect result) {
mTmpProposal.set(availableRect.right - width, availableRect.bottom - height,
availableRect.right, availableRect.bottom);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_LEFT);
+ position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT,
+ result);
}
- private void positionTopLeft(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
+ private void positionTopLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
+ int height, Rect result) {
mTmpProposal.set(availableRect.left, availableRect.top,
availableRect.left + width, availableRect.top + height);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_RIGHT);
+ position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT,
+ result);
}
- private void positionTopRight(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
+ private void positionTopRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
+ int height, Rect result) {
mTmpProposal.set(availableRect.right - width, availableRect.top,
availableRect.right, availableRect.top + height);
- position(task, tasks, availableRect, mTmpProposal, !ALLOW_RESTART,
- SHIFT_POLICY_HORIZONTAL_LEFT);
+ position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT,
+ result);
}
- private void positionCenter(TaskRecord task, ArrayList<TaskRecord> tasks,
- Rect availableRect, int width, int height) {
+ private void positionCenter(ArrayList<TaskRecord> tasks, Rect availableRect, int width,
+ int height, Rect result) {
final int defaultFreeformLeft = getFreeformStartLeft(availableRect);
final int defaultFreeformTop = getFreeformStartTop(availableRect);
mTmpProposal.set(defaultFreeformLeft, defaultFreeformTop,
defaultFreeformLeft + width, defaultFreeformTop + height);
- position(task, tasks, availableRect, mTmpProposal, ALLOW_RESTART,
- SHIFT_POLICY_DIAGONAL_DOWN);
+ position(tasks, availableRect, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN,
+ result);
}
- private void position(TaskRecord task, ArrayList<TaskRecord> tasks, Rect availableRect,
- Rect proposal, boolean allowRestart, int shiftPolicy) {
+ private void position(ArrayList<TaskRecord> tasks, Rect availableRect,
+ Rect proposal, boolean allowRestart, int shiftPolicy, Rect result) {
mTmpOriginal.set(proposal);
boolean restarted = false;
while (boundsConflict(proposal, tasks)) {
@@ -252,7 +260,7 @@ class LaunchingTaskPositioner {
break;
}
}
- task.updateOverrideConfiguration(proposal);
+ result.set(proposal);
}
private boolean shiftedTooFar(Rect start, Rect availableRect, int shiftPolicy) {
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 1c094c174e6a..11daf3f9138b 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -430,7 +430,7 @@ public class LockTaskController {
}
if (andResume) {
- mSupervisor.findTaskToMoveToFrontLocked(task, 0, null, reason,
+ mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
lockTaskModeState != LOCK_TASK_MODE_NONE);
mSupervisor.resumeFocusedStackTopActivityLocked();
mWindowManager.executeAppTransition();
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
index 5d6e9b58881f..1dcb0addc18b 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -17,8 +17,6 @@
package com.android.server.am;
import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
import android.view.WindowManager;
@@ -27,21 +25,19 @@ import android.widget.Toast;
import com.android.internal.R;
/**
- * Helper to manage showing/hiding a image to notify them that they are entering
- * or exiting screen pinning mode.
+ * Helper to manage showing/hiding a image to notify them that they are entering or exiting screen
+ * pinning mode. All exposed methods should be called from a handler thread.
*/
public class LockTaskNotify {
private static final String TAG = "LockTaskNotify";
private static final long SHOW_TOAST_MINIMUM_INTERVAL = 1000;
private final Context mContext;
- private final H mHandler;
private Toast mLastToast;
private long mLastShowToastTime;
public LockTaskNotify(Context context) {
mContext = context;
- mHandler = new H();
}
/** Show "Screen pinned" toast. */
@@ -56,10 +52,6 @@ public class LockTaskNotify {
/** Show a toast that describes the gesture the user should use to escape pinned mode. */
void showEscapeToast() {
- mHandler.obtainMessage(H.SHOW_ESCAPE_TOAST).sendToTarget();
- }
-
- private void handleShowEscapeToast() {
long showToastTime = SystemClock.elapsedRealtime();
if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -79,17 +71,4 @@ public class LockTaskNotify {
toast.show();
return toast;
}
-
- private final class H extends Handler {
- private static final int SHOW_ESCAPE_TOAST = 3;
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case SHOW_ESCAPE_TOAST:
- handleShowEscapeToast();
- break;
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index e3f8410a6788..f31ce74a2209 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -30,7 +30,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
@@ -74,7 +76,6 @@ import static com.android.server.am.proto.TaskRecordProto.MIN_WIDTH;
import static com.android.server.am.proto.TaskRecordProto.ORIG_ACTIVITY;
import static com.android.server.am.proto.TaskRecordProto.REAL_ACTIVITY;
import static com.android.server.am.proto.TaskRecordProto.RESIZE_MODE;
-import static com.android.server.am.proto.TaskRecordProto.RETURN_TO_TYPE;
import static com.android.server.am.proto.TaskRecordProto.STACK_ID;
import static com.android.server.am.proto.TaskRecordProto.ACTIVITY_TYPE;
@@ -172,7 +173,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// Current version of the task record we persist. Used to check if we need to run any upgrade
// code.
private static final int PERSIST_TASK_VERSION = 1;
- private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
static final int INVALID_TASK_ID = -1;
private static final int INVALID_MIN_SIZE = -1;
@@ -187,13 +187,13 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
REPARENT_KEEP_STACK_AT_FRONT,
REPARENT_LEAVE_STACK_IN_PLACE
})
- public @interface ReparentMoveStackMode {}
+ @interface ReparentMoveStackMode {}
// Moves the stack to the front if it was not at the front
- public static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
+ static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
// Only moves the stack to the front if it was focused or front most already
- public static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
+ static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
// Do not move the stack as a part of reparenting
- public static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+ static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
final int taskId; // Unique identifier for this task.
String affinity; // The affinity name for this task, or null; may change identity.
@@ -231,9 +231,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
private boolean mSupportsPictureInPicture; // Whether or not this task and its activities
// support PiP. Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag
// of the root activity.
- boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
- // changes on a temporary basis.
-
/** Can't be put in lockTask mode. */
final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
/** Can enter app pinning with user approval. Can never start over existing lockTask task. */
@@ -268,12 +265,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
* (positive) or back (negative). Absolute value indicates time. */
long mLastTimeMoved = System.currentTimeMillis();
- /** Indication of what to run next when task exits. */
- // TODO: Shouldn't be needed if we have things in visual order. I.e. we stop using stacks or
- // have a stack per standard application type...
- /*@WindowConfiguration.ActivityType*/
- private int mTaskToReturnTo = ACTIVITY_TYPE_STANDARD;
-
/** If original intent did not allow relinquishing task identity, save that information */
private boolean mNeverRelinquishIdentity = true;
@@ -281,7 +272,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// do not want to delete the stack when the task goes empty.
private boolean mReuseTask = false;
- private final String mFilename;
CharSequence lastDescription; // Last description captured for this item.
int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
@@ -327,8 +317,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
mService = service;
- mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
- TaskPersister.IMAGE_EXTENSION;
userId = UserHandle.getUserId(info.applicationInfo.uid);
taskId = _taskId;
lastActiveTime = SystemClock.elapsedRealtime();
@@ -348,8 +336,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
TaskDescription _taskDescription) {
mService = service;
- mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
- TaskPersister.IMAGE_EXTENSION;
userId = UserHandle.getUserId(info.applicationInfo.uid);
taskId = _taskId;
lastActiveTime = SystemClock.elapsedRealtime();
@@ -368,7 +354,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
maxRecents = Math.min(Math.max(info.maxRecents, 1),
ActivityManager.getMaxAppRecentsLimitStatic());
- mTaskToReturnTo = ACTIVITY_TYPE_HOME;
lastTaskDescription = _taskDescription;
touchActiveTime();
mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
@@ -385,8 +370,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
boolean userSetupComplete, int minWidth, int minHeight) {
mService = service;
- mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
- TaskPersister.IMAGE_EXTENSION;
taskId = _taskId;
intent = _intent;
affinityIntent = _affinityIntent;
@@ -401,7 +384,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
isAvailable = true;
autoRemoveRecents = _autoRemoveRecents;
askedCompatMode = _askedCompatMode;
- mTaskToReturnTo = ACTIVITY_TYPE_HOME;
userId = _userId;
mUserSetupComplete = userSetupComplete;
effectiveUid = _effectiveUid;
@@ -707,7 +689,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
} else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
Rect bounds = getLaunchBounds();
if (bounds == null) {
- toStack.layoutTaskInStack(this, null);
+ mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
bounds = mBounds;
}
kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
@@ -904,29 +886,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return this.intent.filterEquals(intent);
}
- void setTaskToReturnTo(/*@WindowConfiguration.ActivityType*/ int taskToReturnTo) {
- mTaskToReturnTo = taskToReturnTo == ACTIVITY_TYPE_RECENTS
- ? ACTIVITY_TYPE_HOME : taskToReturnTo;
- }
-
- void setTaskToReturnTo(ActivityRecord source) {
- if (source.isActivityTypeRecents()) {
- setTaskToReturnTo(ACTIVITY_TYPE_RECENTS);
- } else if (source.isActivityTypeAssistant()) {
- setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT);
- }
- }
-
- int getTaskToReturnTo() {
- return mTaskToReturnTo;
- }
-
- boolean returnsToHomeTask() {
- return mTaskToReturnTo == ACTIVITY_TYPE_HOME;
- }
-
- boolean returnsToStandardTask() {
- return mTaskToReturnTo == ACTIVITY_TYPE_STANDARD;
+ boolean returnsToHomeStack() {
+ final int returnHomeFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME;
+ return (intent.getFlags() & returnHomeFlags) == returnHomeFlags;
}
void setPrevAffiliate(TaskRecord prevAffiliate) {
@@ -1447,17 +1409,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
" mLockTaskAuth=" + lockTaskAuthToString());
}
- boolean isOverHomeStack() {
- return mTaskToReturnTo == ACTIVITY_TYPE_HOME;
- }
-
- boolean isOverAssistantStack() {
- return mTaskToReturnTo == ACTIVITY_TYPE_ASSISTANT;
- }
-
private boolean isResizeable(boolean checkSupportsPip) {
return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
- || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable;
+ || (checkSupportsPip && mSupportsPictureInPicture));
}
boolean isResizeable() {
@@ -2089,7 +2043,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
if (mLastNonFullscreenBounds != null) {
updateOverrideConfiguration(mLastNonFullscreenBounds);
} else {
- inStack.layoutTaskInStack(this, null);
+ mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null);
}
} else {
updateOverrideConfiguration(inStack.mBounds);
@@ -2164,13 +2118,11 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
pw.print(prefix); pw.print("realActivity=");
pw.println(realActivity.flattenToShortString());
}
- if (autoRemoveRecents || isPersistable || !isActivityTypeStandard()
- || mTaskToReturnTo != ACTIVITY_TYPE_STANDARD || numFullscreen != 0) {
+ if (autoRemoveRecents || isPersistable || !isActivityTypeStandard() || numFullscreen != 0) {
pw.print(prefix); pw.print("autoRemoveRecents="); pw.print(autoRemoveRecents);
pw.print(" isPersistable="); pw.print(isPersistable);
pw.print(" numFullscreen="); pw.print(numFullscreen);
- pw.print(" activityType="); pw.print(getActivityType());
- pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
+ pw.print(" activityType="); pw.println(getActivityType());
}
if (rootWasReset || mNeverRelinquishIdentity || mReuseTask
|| mLockTaskAuth != LOCK_TASK_AUTH_PINNABLE) {
@@ -2270,7 +2222,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
}
proto.write(ACTIVITY_TYPE, getActivityType());
- proto.write(RETURN_TO_TYPE, mTaskToReturnTo);
proto.write(RESIZE_MODE, mResizeMode);
proto.write(FULLSCREEN, mFullscreen);
if (mBounds != null) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4aa8adb9dc78..a0c5cfaa7908 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -250,21 +250,21 @@ class UserController implements Handler.Callback {
}
void stopRunningUsersLU(int maxRunningUsers) {
- int num = mUserLru.size();
+ int currentlyRunning = mUserLru.size();
int i = 0;
- while (num > maxRunningUsers && i < mUserLru.size()) {
+ while (currentlyRunning > maxRunningUsers && i < mUserLru.size()) {
Integer oldUserId = mUserLru.get(i);
UserState oldUss = mStartedUsers.get(oldUserId);
if (oldUss == null) {
// Shouldn't happen, but be sane if it does.
mUserLru.remove(i);
- num--;
+ currentlyRunning--;
continue;
}
if (oldUss.state == UserState.STATE_STOPPING
|| oldUss.state == UserState.STATE_SHUTDOWN) {
// This user is already stopping, doesn't count.
- num--;
+ currentlyRunning--;
i++;
continue;
}
@@ -272,16 +272,15 @@ class UserController implements Handler.Callback {
// Owner/System user and current user can't be stopped. We count it as running
// when it is not a pure system user.
if (UserInfo.isSystemOnly(oldUserId)) {
- num--;
+ currentlyRunning--;
}
i++;
continue;
}
// This is a user to be stopped.
- if (stopUsersLU(oldUserId, false, null) != USER_OP_SUCCESS) {
- num--;
+ if (stopUsersLU(oldUserId, false, null) == USER_OP_SUCCESS) {
+ currentlyRunning--;
}
- num--;
i++;
}
}
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
new file mode 100644
index 000000000000..8981db11f7f8
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -0,0 +1,78 @@
+/*
+ * 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.connectivity;
+
+import android.net.LinkProperties;
+import android.net.metrics.DefaultNetworkEvent;
+import android.net.metrics.IpConnectivityLog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tracks events related to the default network for the purpose of default network metrics.
+ * {@hide}
+ */
+public class DefaultNetworkMetrics {
+
+ private static final int ROLLING_LOG_SIZE = 64;
+
+ // Event buffer used for metrics upload. The buffer is cleared when events are collected.
+ @GuardedBy("this")
+ private final List<DefaultNetworkEvent> mEvents = new ArrayList<>();
+
+ public synchronized void listEvents(PrintWriter pw) {
+ long localTimeMs = System.currentTimeMillis();
+ for (DefaultNetworkEvent ev : mEvents) {
+ pw.println(ev);
+ }
+ }
+
+ public synchronized void listEventsAsProto(PrintWriter pw) {
+ for (DefaultNetworkEvent ev : mEvents) {
+ pw.print(IpConnectivityEventBuilder.toProto(ev));
+ }
+ }
+
+ public synchronized void flushEvents(List<IpConnectivityEvent> out) {
+ for (DefaultNetworkEvent ev : mEvents) {
+ out.add(IpConnectivityEventBuilder.toProto(ev));
+ }
+ mEvents.clear();
+ }
+
+ public synchronized void logDefaultNetworkEvent(
+ NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
+ DefaultNetworkEvent ev = new DefaultNetworkEvent();
+ if (newNai != null) {
+ ev.netId = newNai.network().netId;
+ ev.transportTypes = newNai.networkCapabilities.getTransportTypes();
+ }
+ if (prevNai != null) {
+ ev.prevNetId = prevNai.network().netId;
+ final LinkProperties lp = prevNai.linkProperties;
+ ev.prevIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
+ ev.prevIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
+ }
+
+ mEvents.add(ev);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 67e72167faa7..3d71ecb50d9f 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -132,6 +132,18 @@ final public class IpConnectivityEventBuilder {
return out;
}
+ public static IpConnectivityEvent toProto(DefaultNetworkEvent in) {
+ IpConnectivityLogClass.DefaultNetworkEvent ev =
+ new IpConnectivityLogClass.DefaultNetworkEvent();
+ ev.networkId = netIdOf(in.netId);
+ ev.previousNetworkId = netIdOf(in.prevNetId);
+ ev.transportTypes = in.transportTypes;
+ ev.previousNetworkIpSupport = ipSupportOf(in);
+ final IpConnectivityEvent out = buildEvent(in.netId, 0, null);
+ out.setDefaultNetworkEvent(ev);
+ return out;
+ }
+
private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
final IpConnectivityEvent ev = new IpConnectivityEvent();
ev.networkId = netId;
@@ -164,11 +176,6 @@ final public class IpConnectivityEventBuilder {
return true;
}
- if (in instanceof DefaultNetworkEvent) {
- setDefaultNetworkEvent(out, (DefaultNetworkEvent) in);
- return true;
- }
-
if (in instanceof NetworkEvent) {
setNetworkEvent(out, (NetworkEvent) in);
return true;
@@ -225,16 +232,6 @@ final public class IpConnectivityEventBuilder {
out.setIpReachabilityEvent(ipReachabilityEvent);
}
- private static void setDefaultNetworkEvent(IpConnectivityEvent out, DefaultNetworkEvent in) {
- IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent =
- new IpConnectivityLogClass.DefaultNetworkEvent();
- defaultNetworkEvent.networkId = netIdOf(in.netId);
- defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId);
- defaultNetworkEvent.transportTypes = in.transportTypes;
- defaultNetworkEvent.previousNetworkIpSupport = ipSupportOf(in);
- out.setDefaultNetworkEvent(defaultNetworkEvent);
- }
-
private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
IpConnectivityLogClass.NetworkEvent networkEvent =
new IpConnectivityLogClass.NetworkEvent();
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index f2445fa36006..24217e6eef0b 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -32,12 +32,15 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Base64;
import android.util.Log;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.RingBuffer;
import com.android.internal.util.TokenBucket;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -112,6 +115,9 @@ final public class IpConnectivityMetrics extends SystemService {
private final ToIntFunction<Context> mCapacityGetter;
+ @VisibleForTesting
+ final DefaultNetworkMetrics mDefaultNetworkMetrics = new DefaultNetworkMetrics();
+
public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
super(ctx);
mCapacityGetter = capacityGetter;
@@ -135,6 +141,8 @@ final public class IpConnectivityMetrics extends SystemService {
publishBinderService(SERVICE_NAME, impl);
publishBinderService(mNetdListener.SERVICE_NAME, mNetdListener);
+
+ LocalServices.addService(Logger.class, new LoggerImpl());
}
}
@@ -188,6 +196,8 @@ final public class IpConnectivityMetrics extends SystemService {
final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events);
+ mDefaultNetworkMetrics.flushEvents(protoEvents);
+
if (mNetdListener != null) {
mNetdListener.flushStatistics(protoEvents);
}
@@ -228,6 +238,7 @@ final public class IpConnectivityMetrics extends SystemService {
if (mNetdListener != null) {
mNetdListener.listAsProtos(pw);
}
+ mDefaultNetworkMetrics.listEventsAsProto(pw);
return;
}
@@ -237,6 +248,7 @@ final public class IpConnectivityMetrics extends SystemService {
if (mNetdListener != null) {
mNetdListener.list(pw);
}
+ mDefaultNetworkMetrics.listEvents(pw);
}
/**
@@ -254,6 +266,7 @@ final public class IpConnectivityMetrics extends SystemService {
if (mNetdListener != null) {
mNetdListener.list(pw);
}
+ mDefaultNetworkMetrics.listEvents(pw);
}
private void cmdStats(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -366,4 +379,15 @@ final public class IpConnectivityMetrics extends SystemService {
map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
return map;
}
+
+ /** Direct non-Binder interface for event producer clients within the system servers. */
+ public interface Logger {
+ DefaultNetworkMetrics defaultNetworkMetrics();
+ }
+
+ private class LoggerImpl implements Logger {
+ public DefaultNetworkMetrics defaultNetworkMetrics() {
+ return mDefaultNetworkMetrics;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 6206dfcd6622..05c6e69c1895 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -27,6 +27,7 @@ import android.net.metrics.ConnectStats;
import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
+import android.net.metrics.NetworkMetrics;
import android.net.metrics.WakeupEvent;
import android.net.metrics.WakeupStats;
import android.os.RemoteException;
@@ -34,6 +35,7 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.util.ArrayMap;
import android.util.SparseArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
@@ -41,10 +43,11 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.RingBuffer;
import com.android.internal.util.TokenBucket;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
-import java.util.function.Function;
-import java.util.function.IntFunction;
+import java.util.StringJoiner;
/**
* Implementation of the INetdEventListener interface.
@@ -57,13 +60,13 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
private static final boolean DBG = false;
private static final boolean VDBG = false;
- private static final int INITIAL_DNS_BATCH_SIZE = 100;
-
// Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
// bursts of 5000 measurements.
private static final int CONNECT_LATENCY_BURST_LIMIT = 5000;
private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
- private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
+
+ private static final long METRICS_SNAPSHOT_SPAN_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
+ private static final int METRICS_SNAPSHOT_BUFFER_SIZE = 48; // 4 hours
@VisibleForTesting
static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
@@ -72,11 +75,15 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
@VisibleForTesting
static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
- // Sparse arrays of DNS and connect events, grouped by net id.
+ // Array of aggregated DNS and connect events sent by netd, grouped by net id.
+ @GuardedBy("this")
+ private final SparseArray<NetworkMetrics> mNetworkMetrics = new SparseArray<>();
+
@GuardedBy("this")
- private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
+ private final RingBuffer<NetworkMetricsSnapshot> mNetworkMetricsSnapshots =
+ new RingBuffer<>(NetworkMetricsSnapshot.class, METRICS_SNAPSHOT_BUFFER_SIZE);
@GuardedBy("this")
- private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
+ private long mLastSnapshot = 0;
// Array of aggregated wakeup event stats, grouped by interface name.
@GuardedBy("this")
@@ -84,7 +91,7 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
// Ring buffer array for storing packet wake up events sent by Netd.
@GuardedBy("this")
private final RingBuffer<WakeupEvent> mWakeupEvents =
- new RingBuffer(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH);
+ new RingBuffer<>(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH);
private final ConnectivityManager mCm;
@@ -116,6 +123,41 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
mCm = cm;
}
+ private static long projectSnapshotTime(long timeMs) {
+ return (timeMs / METRICS_SNAPSHOT_SPAN_MS) * METRICS_SNAPSHOT_SPAN_MS;
+ }
+
+ private NetworkMetrics getMetricsForNetwork(long timeMs, int netId) {
+ collectPendingMetricsSnapshot(timeMs);
+ NetworkMetrics metrics = mNetworkMetrics.get(netId);
+ if (metrics == null) {
+ // TODO: allow to change transport for a given netid.
+ metrics = new NetworkMetrics(netId, getTransports(netId), mConnectTb);
+ mNetworkMetrics.put(netId, metrics);
+ }
+ return metrics;
+ }
+
+ private NetworkMetricsSnapshot[] getNetworkMetricsSnapshots() {
+ collectPendingMetricsSnapshot(System.currentTimeMillis());
+ return mNetworkMetricsSnapshots.toArray();
+ }
+
+ private void collectPendingMetricsSnapshot(long timeMs) {
+ // Detects time differences larger than the snapshot collection period.
+ // This is robust against clock jumps and long inactivity periods.
+ if (Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) {
+ return;
+ }
+ mLastSnapshot = projectSnapshotTime(timeMs);
+ NetworkMetricsSnapshot snapshot =
+ NetworkMetricsSnapshot.collect(mLastSnapshot, mNetworkMetrics);
+ if (snapshot.stats.isEmpty()) {
+ return;
+ }
+ mNetworkMetricsSnapshots.append(snapshot);
+ }
+
@Override
// Called concurrently by multiple binder threads.
// This method must not block or perform long-running operations.
@@ -124,15 +166,10 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
throws RemoteException {
maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
- DnsEvent dnsEvent = mDnsEvents.get(netId);
- if (dnsEvent == null) {
- dnsEvent = makeDnsEvent(netId);
- mDnsEvents.put(netId, dnsEvent);
- }
- dnsEvent.addResult((byte) eventType, (byte) returnCode, latencyMs);
+ long timestamp = System.currentTimeMillis();
+ getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs);
if (mNetdEventCallback != null) {
- long timestamp = System.currentTimeMillis();
mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
}
}
@@ -144,15 +181,11 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
int port, int uid) throws RemoteException {
maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
- ConnectStats connectStats = mConnectEvents.get(netId);
- if (connectStats == null) {
- connectStats = makeConnectStats(netId);
- mConnectEvents.put(netId, connectStats);
- }
- connectStats.addEvent(error, latencyMs, ipAddr);
+ long timestamp = System.currentTimeMillis();
+ getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr);
if (mNetdEventCallback != null) {
- mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid);
+ mNetdEventCallback.onConnectEvent(ipAddr, port, timestamp, uid);
}
}
@@ -189,11 +222,24 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
}
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
- flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
- flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
+ for (int i = 0; i < mNetworkMetrics.size(); i++) {
+ ConnectStats stats = mNetworkMetrics.valueAt(i).connectMetrics;
+ if (stats.eventCount == 0) {
+ continue;
+ }
+ events.add(IpConnectivityEventBuilder.toProto(stats));
+ }
+ for (int i = 0; i < mNetworkMetrics.size(); i++) {
+ DnsEvent ev = mNetworkMetrics.valueAt(i).dnsMetrics;
+ if (ev.eventCount == 0) {
+ continue;
+ }
+ events.add(IpConnectivityEventBuilder.toProto(ev));
+ }
for (int i = 0; i < mWakeupStats.size(); i++) {
events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
}
+ mNetworkMetrics.clear();
mWakeupStats.clear();
}
@@ -206,8 +252,15 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
}
public synchronized void list(PrintWriter pw) {
- listEvents(pw, mConnectEvents, (x) -> x, "\n");
- listEvents(pw, mDnsEvents, (x) -> x, "\n");
+ for (int i = 0; i < mNetworkMetrics.size(); i++) {
+ pw.println(mNetworkMetrics.valueAt(i).connectMetrics);
+ }
+ for (int i = 0; i < mNetworkMetrics.size(); i++) {
+ pw.println(mNetworkMetrics.valueAt(i).dnsMetrics);
+ }
+ for (NetworkMetricsSnapshot s : getNetworkMetricsSnapshots()) {
+ pw.println(s);
+ }
for (int i = 0; i < mWakeupStats.size(); i++) {
pw.println(mWakeupStats.valueAt(i));
}
@@ -217,41 +270,17 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
}
public synchronized void listAsProtos(PrintWriter pw) {
- listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto, "");
- listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto, "");
- for (int i = 0; i < mWakeupStats.size(); i++) {
- pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+ for (int i = 0; i < mNetworkMetrics.size(); i++) {
+ pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics));
}
- }
-
- private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
- Function<T, IpConnectivityEvent> mapper) {
- for (int i = 0; i < in.size(); i++) {
- out.add(mapper.apply(in.valueAt(i)));
+ for (int i = 0; i < mNetworkMetrics.size(); i++) {
+ pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics));
}
- in.clear();
- }
-
- private static <T> void listEvents(
- PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper, String separator) {
- // Proto derived Classes have toString method that adds a \n at the end.
- // Let the caller control that by passing in the line separator explicitly.
- for (int i = 0; i < events.size(); i++) {
- pw.print(mapper.apply(events.valueAt(i)));
- pw.print(separator);
+ for (int i = 0; i < mWakeupStats.size(); i++) {
+ pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
}
}
- private ConnectStats makeConnectStats(int netId) {
- long transports = getTransports(netId);
- return new ConnectStats(netId, transports, mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
- }
-
- private DnsEvent makeDnsEvent(int netId) {
- long transports = getTransports(netId);
- return new DnsEvent(netId, transports, INITIAL_DNS_BATCH_SIZE);
- }
-
private long getTransports(int netId) {
// TODO: directly query ConnectivityService instead of going through Binder interface.
NetworkCapabilities nc = mCm.getNetworkCapabilities(new Network(netId));
@@ -268,4 +297,32 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
private static void maybeVerboseLog(String s, Object... args) {
if (VDBG) Log.d(TAG, String.format(s, args));
}
+
+ /** Helper class for buffering summaries of NetworkMetrics at regular time intervals */
+ static class NetworkMetricsSnapshot {
+
+ public long timeMs;
+ public List<NetworkMetrics.Summary> stats = new ArrayList<>();
+
+ static NetworkMetricsSnapshot collect(long timeMs, SparseArray<NetworkMetrics> networkMetrics) {
+ NetworkMetricsSnapshot snapshot = new NetworkMetricsSnapshot();
+ snapshot.timeMs = timeMs;
+ for (int i = 0; i < networkMetrics.size(); i++) {
+ NetworkMetrics.Summary s = networkMetrics.valueAt(i).getPendingStats();
+ if (s != null) {
+ snapshot.stats.add(s);
+ }
+ }
+ return snapshot;
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner j = new StringJoiner(", ");
+ for (NetworkMetrics.Summary s : stats) {
+ j.add(s.toString());
+ }
+ return String.format("%tT.%tL: %s", timeMs, timeMs, j.toString());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d7cd81ff3c5f..59870cb97fc9 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -28,6 +28,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static com.android.server.ConnectivityService.SHORT_ARG;
import android.app.Notification;
@@ -60,6 +61,7 @@ import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
+import android.net.util.VersionedBroadcastListener;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Bundle;
@@ -68,6 +70,7 @@ import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
@@ -184,6 +187,8 @@ public class Tethering extends BaseNetworkObserver {
// TODO: Figure out how to merge this and other downstream-tracking objects
// into a single coherent structure.
private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
+ private final VersionedBroadcastListener mCarrierConfigChange;
+ // TODO: Delete SimChangeListener; it's obsolete.
private final SimChangeListener mSimChange;
private volatile TetheringConfiguration mConfig;
@@ -224,11 +229,26 @@ public class Tethering extends BaseNetworkObserver {
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new HashSet<>();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+ mCarrierConfigChange = new VersionedBroadcastListener(
+ "CarrierConfigChangeListener", mContext, smHandler, filter,
+ (Intent ignored) -> {
+ mLog.log("OBSERVED carrier config change");
+ reevaluateSimCardProvisioning();
+ });
+ // TODO: Remove SimChangeListener altogether. For now, we retain it
+ // for logging purposes in case we need to debug something that might
+ // be related to changing signals from ACTION_SIM_STATE_CHANGED to
+ // ACTION_CARRIER_CONFIG_CHANGED.
mSimChange = new SimChangeListener(
- mContext, smHandler, () -> reevaluateSimCardProvisioning());
+ mContext, smHandler, () -> {
+ mLog.log("OBSERVED SIM card change");
+ });
mStateReceiver = new StateReceiver();
- IntentFilter filter = new IntentFilter();
+ filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
@@ -364,18 +384,30 @@ public class Tethering extends BaseNetworkObserver {
return false;
}
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
+ return false;
+ }
+ return (provisionApp.length == 2);
+ }
+
+ // The logic here is aimed solely at confirming that a CarrierConfig exists
+ // and affirms that entitlement checks are not required.
+ //
+ // TODO: find a better way to express this, or alter the checking process
+ // entirely so that this is more intuitive.
+ private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
// Check carrier config for entitlement checks
final CarrierConfigManager configManager = (CarrierConfigManager) mContext
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null && configManager.getConfig() != null) {
- // we do have a CarrierConfigManager and it has a config.
- boolean isEntitlementCheckRequired = configManager.getConfig().getBoolean(
- CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- if (!isEntitlementCheckRequired) {
- return false;
- }
- }
- return (provisionApp.length == 2);
+ if (configManager == null) return false;
+
+ final PersistableBundle carrierConfig = configManager.getConfig();
+ if (carrierConfig == null) return false;
+
+ // A CarrierConfigManager was found and it has a config.
+ final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+ return !isEntitlementCheckRequired;
}
// Used by the SIM card change observation code.
@@ -818,6 +850,7 @@ public class Tethering extends BaseNetworkObserver {
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
handleWifiApAction(intent);
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ mLog.log("OBSERVED configuration changed");
updateConfiguration();
}
}
@@ -1192,6 +1225,7 @@ public class Tethering extends BaseNetworkObserver {
private void reevaluateSimCardProvisioning() {
if (!hasMobileHotspotProvisionApp()) return;
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
ArrayList<Integer> tethered = new ArrayList<>();
synchronized (mPublicSync) {
@@ -1559,6 +1593,7 @@ public class Tethering extends BaseNetworkObserver {
return;
}
+ mCarrierConfigChange.startListening();
mSimChange.startListening();
mUpstreamNetworkMonitor.start();
@@ -1576,6 +1611,7 @@ public class Tethering extends BaseNetworkObserver {
mOffload.stop();
mUpstreamNetworkMonitor.stop();
mSimChange.stopListening();
+ mCarrierConfigChange.stopListening();
notifyDownstreamsOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java b/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
index 3e60f9f6c97a..33c9355ae64b 100644
--- a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
+++ b/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
@@ -23,12 +23,15 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.util.VersionedBroadcastListener;
+import android.net.util.VersionedBroadcastListener.IntentCallback;
import android.os.Handler;
import android.util.Log;
import com.android.internal.telephony.TelephonyIntents;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
@@ -37,88 +40,40 @@ import java.util.concurrent.atomic.AtomicInteger;
*
* @hide
*/
-public class SimChangeListener {
+public class SimChangeListener extends VersionedBroadcastListener {
private static final String TAG = SimChangeListener.class.getSimpleName();
private static final boolean DBG = false;
- private final Context mContext;
- private final Handler mTarget;
- private final AtomicInteger mSimBcastGenerationNumber;
- private final Runnable mCallback;
- private BroadcastReceiver mBroadcastReceiver;
-
public SimChangeListener(Context ctx, Handler handler, Runnable onSimCardLoadedCallback) {
- mContext = ctx;
- mTarget = handler;
- mCallback = onSimCardLoadedCallback;
- mSimBcastGenerationNumber = new AtomicInteger(0);
- }
-
- public int generationNumber() {
- return mSimBcastGenerationNumber.get();
+ super(TAG, ctx, handler, makeIntentFilter(), makeCallback(onSimCardLoadedCallback));
}
- public void startListening() {
- if (DBG) Log.d(TAG, "startListening for SIM changes");
-
- if (mBroadcastReceiver != null) return;
-
- mBroadcastReceiver = new SimChangeBroadcastReceiver(
- mSimBcastGenerationNumber.incrementAndGet());
+ private static IntentFilter makeIntentFilter() {
final IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-
- mContext.registerReceiver(mBroadcastReceiver, filter, null, mTarget);
+ return filter;
}
- public void stopListening() {
- if (DBG) Log.d(TAG, "stopListening for SIM changes");
-
- if (mBroadcastReceiver == null) return;
-
- mSimBcastGenerationNumber.incrementAndGet();
- mContext.unregisterReceiver(mBroadcastReceiver);
- mBroadcastReceiver = null;
- }
-
- private boolean isSimCardLoaded(String state) {
- return INTENT_VALUE_ICC_LOADED.equals(state);
- }
-
- private class SimChangeBroadcastReceiver extends BroadcastReceiver {
- // used to verify this receiver is still current
- final private int mGenerationNumber;
-
- // used to check the sim state transition from non-loaded to loaded
- private boolean mSimNotLoadedSeen = false;
-
- public SimChangeBroadcastReceiver(int generationNumber) {
- mGenerationNumber = generationNumber;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final int currentGenerationNumber = mSimBcastGenerationNumber.get();
-
- if (DBG) {
- Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
- ", current generationNumber=" + currentGenerationNumber);
- }
- if (mGenerationNumber != currentGenerationNumber) return;
-
- final String state = intent.getStringExtra(INTENT_KEY_ICC_STATE);
- Log.d(TAG, "got Sim changed to state " + state + ", mSimNotLoadedSeen=" +
- mSimNotLoadedSeen);
-
- if (!isSimCardLoaded(state)) {
- mSimNotLoadedSeen = true;
- return;
- }
-
- if (mSimNotLoadedSeen) {
- mSimNotLoadedSeen = false;
- mCallback.run();
+ private static Consumer<Intent> makeCallback(Runnable onSimCardLoadedCallback) {
+ return new Consumer<Intent>() {
+ private boolean mSimNotLoadedSeen = false;
+
+ @Override
+ public void accept(Intent intent) {
+ final String state = intent.getStringExtra(INTENT_KEY_ICC_STATE);
+ Log.d(TAG, "got Sim changed to state " + state + ", mSimNotLoadedSeen=" +
+ mSimNotLoadedSeen);
+
+ if (!INTENT_VALUE_ICC_LOADED.equals(state)) {
+ mSimNotLoadedSeen = true;
+ return;
+ }
+
+ if (mSimNotLoadedSeen) {
+ mSimNotLoadedSeen = false;
+ onSimCardLoadedCallback.run();
+ }
}
- }
+ };
}
}
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index d3fd3a992a31..ae01c433fc8d 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -216,7 +216,7 @@ public final class JobServiceContext implements ServiceConnection {
final JobInfo ji = job.getJob();
mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
- isDeadlineExpired, triggeredUris, triggeredAuthorities);
+ isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
// Once we'e begun executing a job, we by definition no longer care whether
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 78367fe97a54..c928c07be983 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -125,6 +125,11 @@ public final class ConnectivityController extends StateController implements
changed |= jobStatus.setUnmeteredConstraintSatisfied(unmetered);
changed |= jobStatus.setNotRoamingConstraintSatisfied(notRoaming);
+ // Pass along the evaluated network for job to use; prevents race
+ // conditions as default routes change over time, and opens the door to
+ // using non-default routes.
+ jobStatus.network = network;
+
// Track system-uid connected/validated as a general reportable proxy for the
// overall state of connectivity constraint satisfiability.
if (jobUid == Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 23caa8cfa701..1a27c0afb5ea 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -22,6 +22,7 @@ import android.app.job.JobInfo;
import android.app.job.JobWorkItem;
import android.content.ClipData;
import android.content.ComponentName;
+import android.net.Network;
import android.net.Uri;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -167,6 +168,7 @@ public final class JobStatus {
// These are filled in by controllers when preparing for execution.
public ArraySet<Uri> changedUris;
public ArraySet<String> changedAuthorities;
+ public Network network;
public int lastEvaluatedPriority;
@@ -1101,6 +1103,9 @@ public final class JobStatus {
}
}
}
+ if (network != null) {
+ pw.print(prefix); pw.print("Network: "); pw.println(network);
+ }
if (pendingWork != null && pendingWork.size() > 0) {
pw.print(prefix); pw.println("Pending work:");
for (int i = 0; i < pendingWork.size(); i++) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index e41c17df8ca1..4cf35bc4accb 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -669,9 +669,11 @@ public class GnssLocationProvider implements LocationProviderInterface {
for (String item : configValues) {
if (DEBUG) Log.d(TAG, "GpsParamsResource: " + item);
// We need to support "KEY =", but not "=VALUE".
- String[] split = item.split("=");
- if (split.length == 2) {
- properties.setProperty(split[0].trim().toUpperCase(), split[1]);
+ int index = item.indexOf("=");
+ if (index > 0 && index + 1 < item.length()) {
+ String key = item.substring(0, index);
+ String value = item.substring(index + 1);
+ properties.setProperty(key.trim().toUpperCase(), value);
} else {
Log.w(TAG, "malformed contents: " + item);
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 6253857d1aa4..03f662a49762 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -310,7 +310,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
String[] libraryDependencies = pkg.usesLibraryFiles;
- if (pkg.isSystemApp()) {
+ if (pkg.isSystem()) {
// For system apps, we want to avoid classpaths checks.
libraryDependencies = NO_LIBRARIES;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cf0ffbb1c2e7..e7b4abb28a4f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -110,7 +110,7 @@ public class PackageDexOptimizer {
}
// We do not dexopt a priv-app package when pm.dexopt.priv-apps is false.
- if (pkg.isPrivilegedApp()) {
+ if (pkg.isPrivileged()) {
return SystemProperties.getBoolean("pm.dexopt.priv-apps", true);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 391deb746e5c..acec2cb0cc79 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -397,7 +397,7 @@ public class PackageManagerService extends IPackageManager.Stub
static final boolean DEBUG_UPGRADE = false;
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
private static final boolean DEBUG_BACKUP = false;
- private static final boolean DEBUG_INSTALL = false;
+ public static final boolean DEBUG_INSTALL = false;
public static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
private static final boolean DEBUG_SHOW_INFO = false;
@@ -522,7 +522,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;
- static final String PLATFORM_PACKAGE_NAME = "android";
+ public static final String PLATFORM_PACKAGE_NAME = "android";
static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
@@ -542,18 +542,6 @@ public class PackageManagerService extends IPackageManager.Stub
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
- /** Permission grant: not grant the permission. */
- private static final int GRANT_DENIED = 1;
-
- /** Permission grant: grant the permission as an install permission. */
- private static final int GRANT_INSTALL = 2;
-
- /** Permission grant: grant the permission as a runtime one. */
- private static final int GRANT_RUNTIME = 3;
-
- /** Permission grant: grant as runtime a permission that was granted as an install time one. */
- private static final int GRANT_UPGRADE = 4;
-
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
@@ -753,9 +741,6 @@ public class PackageManagerService extends IPackageManager.Stub
PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
- // System configuration read by SystemConfig.
- final int[] mGlobalGids;
- final SparseArray<ArraySet<String>> mSystemPermissions;
@GuardedBy("mAvailableFeatures")
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
@@ -938,10 +923,6 @@ public class PackageManagerService extends IPackageManager.Stub
final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
new ArrayMap<ComponentName, PackageParser.Instrumentation>();
- // Mapping from permission names to info about them.
- final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups =
- new ArrayMap<String, PackageParser.PermissionGroup>();
-
// Packages whose data we have transfered into another package, thus
// should no longer exist.
final ArraySet<String> mTransferedPackages = new ArraySet<String>();
@@ -1016,8 +997,6 @@ public class PackageManagerService extends IPackageManager.Stub
private File mCacheDir;
- private ArraySet<String> mPrivappPermissionsViolations;
-
private Future<?> mPrepareAppDataFuture;
private static class IFVerificationParams {
@@ -1405,8 +1384,6 @@ public class PackageManagerService extends IPackageManager.Stub
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
- final boolean mPermissionReviewRequired;
-
private final PackageUsage mPackageUsage = new PackageUsage();
private final CompilerStats mCompilerStats = new CompilerStats();
@@ -1928,9 +1905,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
@Override
- public void onPermissionUpdated(int userId) {
+ public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
synchronized (mPackages) {
- mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+ for (int userId : updatedUserIds) {
+ mSettings.writeRuntimePermissionsForUserLPr(userId, sync);
+ }
}
}
@Override
@@ -2363,9 +2342,6 @@ public class PackageManagerService extends IPackageManager.Stub
mContext = context;
- mPermissionReviewRequired = context.getResources().getBoolean(
- R.bool.config_permissionReviewRequired);
-
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
@@ -2434,8 +2410,6 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
- mGlobalGids = systemConfig.getGlobalGids();
- mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -2872,13 +2846,14 @@ public class PackageManagerService extends IPackageManager.Stub
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
- int updateFlags = UPDATE_PERMISSIONS_ALL;
- if (ver.sdkVersion != mSdkVersion) {
+ final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
+ if (sdkUpdated) {
Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for internal storage");
- updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
+ mPermissionManager.updateAllPermissions(
+ StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
+ mPermissionCallback);
ver.sdkVersion = mSdkVersion;
// If this is the first boot or an update from pre-M, and it is a normal
@@ -3597,7 +3572,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (String packageName : packages) {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
- if (!pkg.isSystemApp()) {
+ if (!pkg.isSystem()) {
Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig <app-link>");
continue;
}
@@ -4228,44 +4203,22 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
int flags) {
- // TODO Move this to PermissionManager when mPermissionGroups is moved there
- synchronized (mPackages) {
- if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
- // This is thrown as NameNotFoundException
- return null;
- }
- }
- return new ParceledListSlice<>(
- mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()));
+ final List<PermissionInfo> permissionList =
+ mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid());
+ return (permissionList == null) ? null : new ParceledListSlice<>(permissionList);
}
@Override
- public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- // reader
- synchronized (mPackages) {
- return PackageParser.generatePermissionGroupInfo(
- mPermissionGroups.get(name), flags);
- }
+ public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
+ return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid());
}
@Override
public @NonNull ParceledListSlice<PermissionGroupInfo> getAllPermissionGroups(int flags) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return ParceledListSlice.emptyList();
- }
- // reader
- synchronized (mPackages) {
- final int N = mPermissionGroups.size();
- ArrayList<PermissionGroupInfo> out
- = new ArrayList<PermissionGroupInfo>(N);
- for (PackageParser.PermissionGroup pg : mPermissionGroups.values()) {
- out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
- }
- return new ParceledListSlice<>(out);
- }
+ final List<PermissionGroupInfo> permissionList =
+ mPermissionManager.getAllPermissionGroups(flags, getCallingUid());
+ return (permissionList == null)
+ ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList);
}
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
@@ -5138,59 +5091,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int checkUidPermission(String permName, int uid) {
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getUserId(callingUid);
- final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
- final boolean isUidInstantApp = getInstantAppPackageName(uid) != null;
- final int userId = UserHandle.getUserId(uid);
- if (!sUserManager.exists(userId)) {
- return PackageManager.PERMISSION_DENIED;
- }
-
- synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
- if (obj != null) {
- if (obj instanceof SharedUserSetting) {
- if (isCallerInstantApp) {
- return PackageManager.PERMISSION_DENIED;
- }
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
- return PackageManager.PERMISSION_DENIED;
- }
- }
- final SettingBase settingBase = (SettingBase) obj;
- final PermissionsState permissionsState = settingBase.getPermissionsState();
- if (permissionsState.hasPermission(permName, userId)) {
- if (isUidInstantApp) {
- if (mSettings.mPermissions.isPermissionInstant(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
- if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
- .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else {
- ArraySet<String> perms = mSystemPermissions.get(uid);
- if (perms != null) {
- if (perms.contains(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
- .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- }
- }
-
- return PackageManager.PERMISSION_DENIED;
+ return mPermissionManager.checkUidPermission(permName, uid, getCallingUid());
}
@Override
@@ -5352,7 +5253,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
synchronized (mPackages) {
- updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL);
+ mPermissionManager.updateAllPermissions(
+ StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
+ mPermissionCallback);
for (int userId : UserManagerService.getInstance().getUserIds()) {
final int packageCount = mPackages.size();
for (int i = 0; i < packageCount; i++) {
@@ -5369,7 +5272,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int getPermissionFlags(String permName, String packageName, int userId) {
- return mPermissionManager.getPermissionFlags(permName, packageName, getCallingUid(), userId);
+ return mPermissionManager.getPermissionFlags(
+ permName, packageName, getCallingUid(), userId);
}
@Override
@@ -9914,7 +9818,7 @@ public class PackageManagerService extends IPackageManager.Stub
// it is better for the user to reinstall than to be in an limbo
// state. Also libs disappearing under an app should never happen
// - just in case.
- if (!pkg.isSystemApp() || pkg.isUpdatedSystemApp()) {
+ if (!pkg.isSystem() || pkg.isUpdatedSystemApp()) {
final int flags = pkg.isUpdatedSystemApp()
? PackageManager.DELETE_KEEP_DATA : 0;
deletePackageLIF(pkg.packageName, null, true, sUserManager.getUserIds(),
@@ -10063,7 +9967,7 @@ public class PackageManagerService extends IPackageManager.Stub
assertPackageIsValid(pkg, policyFlags, scanFlags);
if (Build.IS_DEBUGGABLE &&
- pkg.isPrivilegedApp() &&
+ pkg.isPrivileged() &&
!SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
}
@@ -11156,54 +11060,15 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
}
- N = pkg.permissionGroups.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
- PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
- final String curPackageName = cur == null ? null : cur.info.packageName;
- // Dont allow ephemeral apps to define new permission groups.
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- Slog.w(TAG, "Permission group " + pg.info.name + " from package "
- + pg.info.packageName
- + " ignored: instant apps cannot define new permission groups.");
- continue;
- }
- final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
- if (cur == null || isPackageUpdate) {
- mPermissionGroups.put(pg.info.name, pg);
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- if (isPackageUpdate) {
- r.append("UPD:");
- }
- r.append(pg.info.name);
- }
- } else {
- Slog.w(TAG, "Permission group " + pg.info.name + " from package "
- + pg.info.packageName + " ignored: original from "
- + cur.info.packageName);
- if (chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append("DUP:");
- r.append(pg.info.name);
- }
- }
- }
- if (r != null) {
- if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r);
+ // Don't allow ephemeral applications to define new permissions groups.
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ Slog.w(TAG, "Permission groups from package " + pkg.packageName
+ + " ignored: instant apps cannot define new permission groups.");
+ } else {
+ mPermissionManager.addAllPermissionGroups(pkg, chatty);
}
-
- // Dont allow ephemeral apps to define new permissions.
+ // Don't allow ephemeral applications to define new permissions.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
Slog.w(TAG, "Permissions from package " + pkg.packageName
+ " ignored: instant apps cannot define new permissions.");
@@ -11995,611 +11860,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- public static final int UPDATE_PERMISSIONS_ALL = 1<<0;
- public static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
- public static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
-
- private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
- // Update the parent permissions
- updatePermissionsLPw(pkg.packageName, pkg, flags);
- // Update the child permissions
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- PackageParser.Package childPkg = pkg.childPackages.get(i);
- updatePermissionsLPw(childPkg.packageName, childPkg, flags);
- }
- }
-
- private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
- int flags) {
- final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
- updatePermissionsLocked(changingPkg, pkgInfo, volumeUuid, flags);
- }
-
- private void updatePermissionsLocked(String changingPkg,
- PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
- // TODO: Most of the methods exposing BasePermission internals [source package name,
- // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
- // have package settings, we should make note of it elsewhere [map between
- // source package name and BasePermission] and cycle through that here. Then we
- // define a single method on BasePermission that takes a PackageSetting, changing
- // package name and a package.
- // NOTE: With this approach, we also don't need to tree trees differently than
- // normal permissions. Today, we need two separate loops because these BasePermission
- // objects are stored separately.
- // Make sure there are no dangling permission trees.
- flags = mPermissionManager.updatePermissionTrees(changingPkg, pkgInfo, flags);
-
- // Make sure all dynamic permissions have been assigned to a package,
- // and make sure there are no dangling permissions.
- flags = mPermissionManager.updatePermissions(changingPkg, pkgInfo, flags);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
- // Now update the permissions for all packages, in particular
- // replace the granted permissions of the system packages.
- if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
- for (PackageParser.Package pkg : mPackages.values()) {
- if (pkg != pkgInfo) {
- // Only replace for packages on requested volume
- final String volumeUuid = getVolumeUuidForPackage(pkg);
- final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
- && Objects.equals(replaceVolumeUuid, volumeUuid);
- grantPermissionsLPw(pkg, replace, changingPkg);
- }
- }
- }
-
- if (pkgInfo != null) {
- // Only replace for packages on requested volume
- final String volumeUuid = getVolumeUuidForPackage(pkgInfo);
- final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
- && Objects.equals(replaceVolumeUuid, volumeUuid);
- grantPermissionsLPw(pkgInfo, replace, changingPkg);
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
- String packageOfInterest) {
- // IMPORTANT: There are two types of permissions: install and runtime.
- // Install time permissions are granted when the app is installed to
- // all device users and users added in the future. Runtime permissions
- // are granted at runtime explicitly to specific users. Normal and signature
- // protected permissions are install time permissions. Dangerous permissions
- // are install permissions if the app's target SDK is Lollipop MR1 or older,
- // otherwise they are runtime permissions. This function does not manage
- // runtime permissions except for the case an app targeting Lollipop MR1
- // being upgraded to target a newer SDK, in which case dangerous permissions
- // are transformed from install time to runtime ones.
-
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
- }
-
- PermissionsState permissionsState = ps.getPermissionsState();
- PermissionsState origPermissions = permissionsState;
-
- final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
-
- boolean runtimePermissionsRevoked = false;
- int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY;
-
- boolean changedInstallPermission = false;
-
- if (replace) {
- ps.installPermissionsFixed = false;
- if (!ps.isSharedUser()) {
- origPermissions = new PermissionsState(permissionsState);
- permissionsState.reset();
- } else {
- // We need to know only about runtime permission changes since the
- // calling code always writes the install permissions state but
- // the runtime ones are written only if changed. The only cases of
- // changed runtime permissions here are promotion of an install to
- // runtime and revocation of a runtime from a shared user.
- changedRuntimePermissionUserIds =
- mPermissionManager.revokeUnusedSharedUserPermissions(
- ps.sharedUser, UserManagerService.getInstance().getUserIds());
- if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) {
- runtimePermissionsRevoked = true;
- }
- }
- }
-
- permissionsState.setGlobalGids(mGlobalGids);
-
- final int N = pkg.requestedPermissions.size();
- for (int i=0; i<N; i++) {
- final String name = pkg.requestedPermissions.get(i);
- final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(name);
- final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
- >= Build.VERSION_CODES.M;
-
- if (DEBUG_INSTALL) {
- Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
- }
-
- if (bp == null || bp.getSourcePackageSetting() == null) {
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Unknown permission " + name
- + " in package " + pkg.packageName);
- }
- }
- continue;
- }
-
-
- // Limit ephemeral apps to ephemeral allowed permissions.
- if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
- if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() + " for package "
- + pkg.packageName);
- }
- continue;
- }
-
- if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
- if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying runtime-only permission " + bp.getName() + " for package "
- + pkg.packageName);
- }
- continue;
- }
-
- final String perm = bp.getName();
- boolean allowedSig = false;
- int grant = GRANT_DENIED;
-
- // Keep track of app op permissions.
- if (bp.isAppOp()) {
- mSettings.addAppOpPackage(perm, pkg.packageName);
- }
-
- if (bp.isNormal()) {
- // For all apps normal permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (bp.isRuntime()) {
- // If a permission review is required for legacy apps we represent
- // their permissions as always granted runtime ones since we need
- // to keep the review required permission flag per user while an
- // install permission's state is shared across all users.
- if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) {
- // For legacy apps dangerous permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (origPermissions.hasInstallPermission(bp.getName())) {
- // For legacy apps that became modern, install becomes runtime.
- grant = GRANT_UPGRADE;
- } else if (mPromoteSystemApps
- && isSystemApp(ps)
- && mExistingSystemPackages.contains(ps.name)) {
- // For legacy system apps, install becomes runtime.
- // We cannot check hasInstallPermission() for system apps since those
- // permissions were granted implicitly and not persisted pre-M.
- grant = GRANT_UPGRADE;
- } else {
- // For modern apps keep runtime permissions unchanged.
- grant = GRANT_RUNTIME;
- }
- } else if (bp.isSignature()) {
- // For all apps signature permissions are install time ones.
- allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
- if (allowedSig) {
- grant = GRANT_INSTALL;
- }
- }
-
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Granting permission " + perm + " to package " + pkg.packageName);
- }
-
- if (grant != GRANT_DENIED) {
- if (!isSystemApp(ps) && ps.installPermissionsFixed) {
- // If this is an existing, non-system package, then
- // we can't add any new permissions to it.
- if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
- // Except... if this is a permission that was added
- // to the platform (note: need to only do this when
- // updating the platform).
- if (!isNewPlatformPermissionForPackage(perm, pkg)) {
- grant = GRANT_DENIED;
- }
- }
- }
-
- switch (grant) {
- case GRANT_INSTALL: {
- // Revoke this as runtime permission to handle the case of
- // a runtime permission being downgraded to an install one.
- // Also in permission review mode we keep dangerous permissions
- // for legacy apps
- for (int userId : UserManagerService.getInstance().getUserIds()) {
- if (origPermissions.getRuntimePermissionState(
- perm, userId) != null) {
- // Revoke the runtime permission and clear the flags.
- origPermissions.revokeRuntimePermission(bp, userId);
- origPermissions.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS, 0);
- // If we revoked a permission permission, we have to write.
- changedRuntimePermissionUserIds = ArrayUtils.appendInt(
- changedRuntimePermissionUserIds, userId);
- }
- }
- // Grant an install permission.
- if (permissionsState.grantInstallPermission(bp) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- changedInstallPermission = true;
- }
- } break;
-
- case GRANT_RUNTIME: {
- // Grant previously granted runtime permissions.
- for (int userId : UserManagerService.getInstance().getUserIds()) {
- PermissionState permissionState = origPermissions
- .getRuntimePermissionState(perm, userId);
- int flags = permissionState != null
- ? permissionState.getFlags() : 0;
- if (origPermissions.hasRuntimePermission(perm, userId)) {
- // Don't propagate the permission in a permission review mode if
- // the former was revoked, i.e. marked to not propagate on upgrade.
- // Note that in a permission review mode install permissions are
- // represented as constantly granted runtime ones since we need to
- // keep a per user state associated with the permission. Also the
- // revoke on upgrade flag is no longer applicable and is reset.
- final boolean revokeOnUpgrade = (flags & PackageManager
- .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
- if (revokeOnUpgrade) {
- flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
- // Since we changed the flags, we have to write.
- changedRuntimePermissionUserIds = ArrayUtils.appendInt(
- changedRuntimePermissionUserIds, userId);
- }
- if (!mPermissionReviewRequired || !revokeOnUpgrade) {
- if (permissionsState.grantRuntimePermission(bp, userId) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- // If we cannot put the permission as it was,
- // we have to write.
- changedRuntimePermissionUserIds = ArrayUtils.appendInt(
- changedRuntimePermissionUserIds, userId);
- }
- }
-
- // If the app supports runtime permissions no need for a review.
- if (mPermissionReviewRequired
- && appSupportsRuntimePermissions
- && (flags & PackageManager
- .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- // Since we changed the flags, we have to write.
- changedRuntimePermissionUserIds = ArrayUtils.appendInt(
- changedRuntimePermissionUserIds, userId);
- }
- } else if (mPermissionReviewRequired
- && !appSupportsRuntimePermissions) {
- // For legacy apps that need a permission review, every new
- // runtime permission is granted but it is pending a review.
- // We also need to review only platform defined runtime
- // permissions as these are the only ones the platform knows
- // how to disable the API to simulate revocation as legacy
- // apps don't expect to run with revoked permissions.
- if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
- if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
- flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
- // We changed the flags, hence have to write.
- changedRuntimePermissionUserIds = ArrayUtils.appendInt(
- changedRuntimePermissionUserIds, userId);
- }
- }
- if (permissionsState.grantRuntimePermission(bp, userId)
- != PermissionsState.PERMISSION_OPERATION_FAILURE) {
- // We changed the permission, hence have to write.
- changedRuntimePermissionUserIds = ArrayUtils.appendInt(
- changedRuntimePermissionUserIds, userId);
- }
- }
- // Propagate the permission flags.
- permissionsState.updatePermissionFlags(bp, userId, flags, flags);
- }
- } break;
-
- case GRANT_UPGRADE: {
- // Grant runtime permissions for a previously held install permission.
- PermissionState permissionState = origPermissions
- .getInstallPermissionState(perm);
- final int flags = permissionState != null ? permissionState.getFlags() : 0;
-
- if (origPermissions.revokeInstallPermission(bp)
- != PermissionsState.PERMISSION_OPERATION_FAILURE) {
- // We will be transferring the permission flags, so clear them.
- origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS, 0);
- changedInstallPermission = true;
- }
-
- // If the permission is not to be promoted to runtime we ignore it and
- // also its other flags as they are not applicable to install permissions.
- if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
- for (int userId : currentUserIds) {
- if (permissionsState.grantRuntimePermission(bp, userId) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- // Transfer the permission flags.
- permissionsState.updatePermissionFlags(bp, userId,
- flags, flags);
- // If we granted the permission, we have to write.
- changedRuntimePermissionUserIds = ArrayUtils.appendInt(
- changedRuntimePermissionUserIds, userId);
- }
- }
- }
- } break;
-
- default: {
- if (packageOfInterest == null
- || packageOfInterest.equals(pkg.packageName)) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " because it was previously installed without");
- }
- }
- } break;
- }
- } else {
- if (permissionsState.revokeInstallPermission(bp) !=
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- // Also drop the permission flags.
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS, 0);
- changedInstallPermission = true;
- Slog.i(TAG, "Un-granting permission " + perm
- + " from package " + pkg.packageName
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
- + ")");
- } else if (bp.isAppOp()) {
- // Don't print warning for app op permissions, since it is fine for them
- // not to be granted, there is a UI for the user to decide.
- if (DEBUG_PERMISSIONS
- && (packageOfInterest == null
- || packageOfInterest.equals(pkg.packageName))) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
- + ")");
- }
- }
- }
- }
-
- if ((changedInstallPermission || replace) && !ps.installPermissionsFixed &&
- !isSystemApp(ps) || isUpdatedSystemApp(ps)){
- // This is the first that we have heard about this package, so the
- // permissions we have now selected are fixed until explicitly
- // changed.
- ps.installPermissionsFixed = true;
- }
-
- // Persist the runtime permissions state for users with changes. If permissions
- // were revoked because no app in the shared user declares them we have to
- // write synchronously to avoid losing runtime permissions state.
- for (int userId : changedRuntimePermissionUserIds) {
- mSettings.writeRuntimePermissionsForUserLPr(userId, runtimePermissionsRevoked);
- }
- }
-
- private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
- boolean allowed = false;
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- for (int ip=0; ip<NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (npi.name.equals(perm)
- && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) {
- allowed = true;
- Log.i(TAG, "Auto-granting " + perm + " to old pkg "
- + pkg.packageName);
- break;
- }
- }
- return allowed;
- }
-
- /**
- * Determines whether a package is whitelisted for a particular privapp permission.
- *
- * <p>Does NOT check whether the package is a privapp, just whether it's whitelisted.
- *
- * <p>This handles parent/child apps.
- */
- private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) {
- ArraySet<String> wlPermissions = SystemConfig.getInstance()
- .getPrivAppPermissions(pkg.packageName);
- // Let's check if this package is whitelisted...
- boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm);
- // If it's not, we'll also tail-recurse to the parent.
- return whitelisted ||
- pkg.parentPackage != null && hasPrivappWhitelistEntry(perm, pkg.parentPackage);
- }
-
- private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
- BasePermission bp, PermissionsState origPermissions) {
- boolean oemPermission = bp.isOEM();
- boolean privilegedPermission = bp.isPrivileged();
- boolean privappPermissionsDisable =
- RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
- boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
- boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);
- if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivilegedApp()
- && !platformPackage && platformPermission) {
- if (!hasPrivappWhitelistEntry(perm, pkg)) {
- Slog.w(TAG, "Privileged permission " + perm + " for package "
- + pkg.packageName + " - not in privapp-permissions whitelist");
- // Only report violations for apps on system image
- if (!mSystemReady && !pkg.isUpdatedSystemApp()) {
- // it's only a reportable violation if the permission isn't explicitly denied
- final ArraySet<String> deniedPermissions = SystemConfig.getInstance()
- .getPrivAppDenyPermissions(pkg.packageName);
- final boolean permissionViolation =
- deniedPermissions == null || !deniedPermissions.contains(perm);
- if (permissionViolation
- && RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
- if (mPrivappPermissionsViolations == null) {
- mPrivappPermissionsViolations = new ArraySet<>();
- }
- mPrivappPermissionsViolations.add(pkg.packageName + ": " + perm);
- } else {
- return false;
- }
- }
- if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
- return false;
- }
- }
- }
- boolean allowed = (compareSignatures(
- bp.getSourcePackageSetting().signatures.mSignatures, pkg.mSignatures)
- == PackageManager.SIGNATURE_MATCH)
- || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
- == PackageManager.SIGNATURE_MATCH);
- if (!allowed && (privilegedPermission || oemPermission)) {
- if (isSystemApp(pkg)) {
- // For updated system applications, a privileged/oem permission
- // is granted only if it had been defined by the original application.
- if (pkg.isUpdatedSystemApp()) {
- final PackageSetting sysPs = mSettings
- .getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPs != null && sysPs.getPermissionsState().hasInstallPermission(perm)) {
- // If the original was granted this permission, we take
- // that grant decision as read and propagate it to the
- // update.
- if ((privilegedPermission && sysPs.isPrivileged())
- || (oemPermission && sysPs.isOem()
- && canGrantOemPermission(sysPs, perm))) {
- allowed = true;
- }
- } else {
- // The system apk may have been updated with an older
- // version of the one on the data partition, but which
- // granted a new system permission that it didn't have
- // before. In this case we do want to allow the app to
- // now get the new permission if the ancestral apk is
- // privileged to get it.
- if (sysPs != null && sysPs.pkg != null
- && isPackageRequestingPermission(sysPs.pkg, perm)
- && ((privilegedPermission && sysPs.isPrivileged())
- || (oemPermission && sysPs.isOem()
- && canGrantOemPermission(sysPs, perm)))) {
- allowed = true;
- }
- // Also if a privileged parent package on the system image or any of
- // its children requested a privileged/oem permission, the updated child
- // packages can also get the permission.
- if (pkg.parentPackage != null) {
- final PackageSetting disabledSysParentPs = mSettings
- .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
- final PackageParser.Package disabledSysParentPkg =
- (disabledSysParentPs == null || disabledSysParentPs.pkg == null)
- ? null : disabledSysParentPs.pkg;
- if (disabledSysParentPkg != null
- && ((privilegedPermission && disabledSysParentPs.isPrivileged())
- || (oemPermission && disabledSysParentPs.isOem()))) {
- if (isPackageRequestingPermission(disabledSysParentPkg, perm)
- && canGrantOemPermission(disabledSysParentPs, perm)) {
- allowed = true;
- } else if (disabledSysParentPkg.childPackages != null) {
- final int count = disabledSysParentPkg.childPackages.size();
- for (int i = 0; i < count; i++) {
- final PackageParser.Package disabledSysChildPkg =
- disabledSysParentPkg.childPackages.get(i);
- final PackageSetting disabledSysChildPs =
- mSettings.getDisabledSystemPkgLPr(
- disabledSysChildPkg.packageName);
- if (isPackageRequestingPermission(disabledSysChildPkg, perm)
- && canGrantOemPermission(
- disabledSysChildPs, perm)) {
- allowed = true;
- break;
- }
- }
- }
- }
- }
- }
- } else {
- allowed = (privilegedPermission && isPrivilegedApp(pkg))
- || (oemPermission && isOemApp(pkg)
- && canGrantOemPermission(
- mSettings.getPackageLPr(pkg.packageName), perm));
- }
- }
- }
- if (!allowed) {
- if (!allowed
- && bp.isPre23()
- && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
- // If this was a previously normal/dangerous permission that got moved
- // to a system permission as part of the runtime permission redesign, then
- // we still want to blindly grant it to old apps.
- allowed = true;
- }
- if (!allowed && bp.isInstaller()
- && pkg.packageName.equals(mRequiredInstallerPackage)) {
- // If this permission is to be granted to the system installer and
- // this app is an installer, then it gets the permission.
- allowed = true;
- }
- if (!allowed && bp.isVerifier()
- && pkg.packageName.equals(mRequiredVerifierPackage)) {
- // If this permission is to be granted to the system verifier and
- // this app is a verifier, then it gets the permission.
- allowed = true;
- }
- if (!allowed && bp.isPreInstalled()
- && isSystemApp(pkg)) {
- // Any pre-installed system app is allowed to get this permission.
- allowed = true;
- }
- if (!allowed && bp.isDevelopment()) {
- // For development permissions, a development permission
- // is granted only if it was already granted.
- allowed = origPermissions.hasInstallPermission(perm);
- }
- if (!allowed && bp.isSetup()
- && pkg.packageName.equals(mSetupWizardPackage)) {
- // If this permission is to be granted to the system setup wizard and
- // this app is a setup wizard, then it gets the permission.
- allowed = true;
- }
- }
- return allowed;
- }
-
- private static boolean canGrantOemPermission(PackageSetting ps, String permission) {
- if (!ps.isOem()) {
- return false;
- }
- // all oem permissions must explicitly be granted or denied
- final Boolean granted =
- SystemConfig.getInstance().getOemPermissions(ps.name).get(permission);
- if (granted == null) {
- throw new IllegalStateException("OEM permission" + permission + " requested by package "
- + ps.name + " must be explicitly declared granted or not");
- }
- return Boolean.TRUE == granted;
- }
-
- private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
- final int permCount = pkg.requestedPermissions.size();
- for (int j = 0; j < permCount; j++) {
- String requestedPermission = pkg.requestedPermissions.get(j);
- if (permission.equals(requestedPermission)) {
- return true;
- }
- }
- return false;
- }
final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
@@ -16491,7 +15751,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// don't allow a system upgrade unless the upgrade hash matches
- if (oldPackage.restrictUpdateHash != null && oldPackage.isSystemApp()) {
+ if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
byte[] digestBytes = null;
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-512");
@@ -16758,7 +16018,9 @@ public class PackageManagerService extends IPackageManager.Stub
setInstallerPackageNameLPw(deletedPackage, installerPackageName);
// Update permissions for restored package
- updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+ mPermissionManager.updatePermissions(
+ deletedPackage.packageName, deletedPackage, false, mPackages.values(),
+ mPermissionCallback);
mSettings.writeLPr();
}
@@ -16900,7 +16162,9 @@ public class PackageManagerService extends IPackageManager.Stub
setInstallerPackageNameLPw(deletedPackage, installerPackageName);
// Update permissions for restored package
- updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+ mPermissionManager.updatePermissions(
+ deletedPackage.packageName, deletedPackage, false, mPackages.values(),
+ mPermissionCallback);
mSettings.writeLPr();
}
@@ -17013,12 +16277,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void updateSettingsInternalLI(PackageParser.Package newPackage,
+ private void updateSettingsInternalLI(PackageParser.Package pkg,
String installerPackageName, int[] allUsers, int[] installedForUsers,
PackageInstalledInfo res, UserHandle user, int installReason) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
- String pkgName = newPackage.packageName;
+ String pkgName = pkg.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
//note that the new package setting would have already been
@@ -17030,18 +16294,18 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
+ if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.codePath);
synchronized (mPackages) {
- updatePermissionsLPw(newPackage.packageName, newPackage,
- UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
- ? UPDATE_PERMISSIONS_ALL : 0));
+// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
+ mPermissionManager.updatePermissions(pkg.packageName, pkg, true, mPackages.values(),
+ mPermissionCallback);
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
PackageSetting ps = mSettings.mPackages.get(pkgName);
final int userId = user.getIdentifier();
if (ps != null) {
- if (isSystemApp(newPackage)) {
+ if (isSystemApp(pkg)) {
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
@@ -17101,8 +16365,8 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.writeKernelMappingLPr(ps);
}
res.name = pkgName;
- res.uid = newPackage.applicationInfo.uid;
- res.pkg = newPackage;
+ res.uid = pkg.applicationInfo.uid;
+ res.pkg = pkg;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
@@ -17801,18 +17065,6 @@ public class PackageManagerService extends IPackageManager.Stub
return installFlags;
}
- private String getVolumeUuidForPackage(PackageParser.Package pkg) {
- if (isExternal(pkg)) {
- if (TextUtils.isEmpty(pkg.volumeUuid)) {
- return StorageManager.UUID_PRIMARY_PHYSICAL;
- } else {
- return pkg.volumeUuid;
- }
- } else {
- return StorageManager.UUID_PRIVATE_INTERNAL;
- }
- }
-
private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
if (isExternal(pkg)) {
if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -18457,7 +17709,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (outInfo != null) {
outInfo.removedAppId = removedAppId;
}
- updatePermissionsLPw(deletedPs.name, null, 0);
+ mPermissionManager.updatePermissions(
+ deletedPs.name, null, false, mPackages.values(), mPermissionCallback);
if (deletedPs.sharedUser != null) {
// Remove permissions associated with package. Since runtime
// permissions are per user we have to kill the removed package
@@ -18660,21 +17913,21 @@ public class PackageManagerService extends IPackageManager.Stub
parseFlags |= PackageParser.PARSE_IS_OEM;
}
- final PackageParser.Package newPkg =
+ final PackageParser.Package pkg =
scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/, 0 /*currentTime*/, null);
try {
// update shared libraries for the newly re-installed system package
- updateSharedLibrariesLPr(newPkg, null);
+ updateSharedLibrariesLPr(pkg, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
- prepareAppDataAfterInstallLIF(newPkg);
+ prepareAppDataAfterInstallLIF(pkg);
// writer
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(newPkg.packageName);
+ PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
// Propagate the permissions state as we do not want to drop on the floor
// runtime permissions. The update permissions method below will take
@@ -18682,8 +17935,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (origPermissionState != null) {
ps.getPermissionsState().copyFrom(origPermissionState);
}
- updatePermissionsLPw(newPkg.packageName, newPkg,
- UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
+ mPermissionManager.updatePermissions(pkg.packageName, pkg, true, mPackages.values(),
+ mPermissionCallback);
final boolean applyUserRestrictions
= (allUserHandles != null) && (origUserHandles != null);
@@ -18716,7 +17969,7 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.writeLPr();
}
}
- return newPkg;
+ return pkg;
}
private boolean deleteInstalledPackageLIF(PackageSetting ps,
@@ -19333,7 +18586,7 @@ public class PackageManagerService extends IPackageManager.Stub
// If permission review is enabled and this is a legacy app, mark the
// permission as requiring a review as this is the initial state.
int flags = 0;
- if (mPermissionReviewRequired
+ if (mSettings.mPermissions.mPermissionReviewRequired
&& ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
}
@@ -20692,7 +19945,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
// data partition and then replace the version on the system partition.
final PackageParser.Package deletedPkg = pkgSetting.pkg;
final boolean isSystemStub = deletedPkg.isStub
- && deletedPkg.isSystemApp();
+ && deletedPkg.isSystem();
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
@@ -20732,22 +19985,23 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
synchronized (mPackages) {
disableSystemPackageLPw(deletedPkg, tmpPkg);
}
- final PackageParser.Package newPkg;
+ final PackageParser.Package pkg;
try (PackageFreezer freezer =
freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE;
- newPkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,
+ pkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,
0 /*currentTime*/, null /*user*/);
- prepareAppDataAfterInstallLIF(newPkg);
+ prepareAppDataAfterInstallLIF(pkg);
synchronized (mPackages) {
try {
- updateSharedLibrariesLPr(newPkg, null);
+ updateSharedLibrariesLPr(pkg, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
}
- updatePermissionsLPw(newPkg.packageName, newPkg,
- UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
+ mPermissionManager.updatePermissions(
+ pkg.packageName, pkg, true, mPackages.values(),
+ mPermissionCallback);
mSettings.writeLPr();
}
} catch (PackageManagerException e) {
@@ -20787,11 +20041,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
return;
}
- clearAppDataLIF(newPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
+ clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
| FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- clearAppProfilesLIF(newPkg, UserHandle.USER_ALL);
- mDexManager.notifyPackageUpdated(newPkg.packageName,
- newPkg.baseCodePath, newPkg.splitCodePaths);
+ clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
+ mDexManager.notifyPackageUpdated(pkg.packageName,
+ pkg.baseCodePath, pkg.splitCodePaths);
}
}
if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
@@ -21102,8 +20356,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
// permissions, ensure permissions are updated. Beware of dragons if you
// try optimizing this.
synchronized (mPackages) {
- updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
- UPDATE_PERMISSIONS_ALL);
+ mPermissionManager.updateAllPermissions(
+ StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
+ mPermissionCallback);
}
// Kick off any messages waiting for system ready
@@ -21152,10 +20407,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
- if (mPrivappPermissionsViolations != null) {
- throw new IllegalStateException("Signature|privileged permissions not in "
- + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
- }
+ mPermissionManager.systemReady();
}
public void waitForAppDataPrepared() {
@@ -22140,13 +21392,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
synchronized (mPackages) {
- int updateFlags = UPDATE_PERMISSIONS_ALL;
- if (ver.sdkVersion != mSdkVersion) {
+ final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
+ if (sdkUpdated) {
logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for " + volumeUuid);
- updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLocked(null, null, volumeUuid, updateFlags);
+ mPermissionManager.updateAllPermissions(volumeUuid, sdkUpdated, mPackages.values(),
+ mPermissionCallback);
// Yay, everything is now upgraded
ver.forceCurrent();
@@ -22594,7 +21846,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
* requested by the app.
*/
private boolean maybeMigrateAppDataLIF(PackageParser.Package pkg, int userId) {
- if (pkg.isSystemApp() && !StorageManager.isFileEncryptedNativeOrEmulated()
+ if (pkg.isSystem() && !StorageManager.isFileEncryptedNativeOrEmulated()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
final int storageTarget = pkg.applicationInfo.isDefaultToDeviceProtectedStorage()
? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
@@ -23159,9 +22411,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
// permissions to keep per user flag state whether review is needed.
// Hence, if a new user is added we have to propagate dangerous
// permission grants for these legacy apps.
- if (mPermissionReviewRequired) {
- updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
- | UPDATE_PERMISSIONS_REPLACE_ALL);
+ if (mSettings.mPermissions.mPermissionReviewRequired) {
+// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
+ mPermissionManager.updateAllPermissions(
+ StorageManager.UUID_PRIVATE_INTERNAL, true, mPackages.values(),
+ mPermissionCallback);
}
}
}
@@ -23606,13 +22860,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
@Override
- public PackageParser.PermissionGroup getPermissionGroupTEMP(String groupName) {
- synchronized (mPackages) {
- return mPermissionGroups.get(groupName);
- }
- }
-
- @Override
public boolean isInstantApp(String packageName, int userId) {
return PackageManagerService.this.isInstantApp(packageName, userId);
}
@@ -23750,24 +22997,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
@Override
public boolean isPermissionsReviewRequired(String packageName, int userId) {
synchronized (mPackages) {
- // If we do not support permission review, done.
- if (!mPermissionReviewRequired) {
- return false;
- }
-
- PackageSetting packageSetting = mSettings.mPackages.get(packageName);
- if (packageSetting == null) {
- return false;
- }
-
- // Permission review applies only to apps not supporting the new permission model.
- if (packageSetting.pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
- return false;
- }
-
- // Legacy apps have the permission and get user consent on launch.
- PermissionsState permissionsState = packageSetting.getPermissionsState();
- return permissionsState.isPermissionReviewRequired(userId);
+ return mPermissionManager.isPermissionsReviewRequired(
+ mPackages.get(packageName), userId);
}
}
@@ -23921,6 +23152,16 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
@Override
+ public boolean isLegacySystemApp(Package pkg) {
+ synchronized (mPackages) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ return mPromoteSystemApps
+ && ps.isSystem()
+ && mExistingSystemPackages.contains(ps.name);
+ }
+ }
+
+ @Override
public List<PackageInfo> getOverlayPackages(int userId) {
final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>();
synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1fea003a9cc1..a2099e6080e5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -16,14 +16,17 @@
package com.android.server.pm;
+import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
@@ -41,6 +44,7 @@ import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.content.res.AssetManager;
import android.content.res.Resources;
@@ -49,15 +53,23 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.IUserManager;
+import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
-import android.util.ArrayMap;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.PrintWriterPrinter;
+
import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.SizedInputStream;
import com.android.server.SystemConfig;
@@ -81,6 +93,12 @@ import java.util.WeakHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+
class PackageManagerShellCommand extends ShellCommand {
/** Path for streaming APK content */
private static final String STDIN_PATH = "-";
@@ -107,6 +125,20 @@ class PackageManagerShellCommand extends ShellCommand {
final PrintWriter pw = getOutPrintWriter();
try {
switch(cmd) {
+ case "path":
+ return runPath();
+ case "dump":
+ return runDump();
+ case "list":
+ return runList();
+ case "resolve-activity":
+ return runResolveActivity();
+ case "query-activities":
+ return runQueryIntentActivities();
+ case "query-services":
+ return runQueryIntentServices();
+ case "query-receivers":
+ return runQueryIntentReceivers();
case "install":
return runInstall();
case "install-abandon":
@@ -122,44 +154,99 @@ class PackageManagerShellCommand extends ShellCommand {
return runInstallWrite();
case "install-existing":
return runInstallExisting();
+ case "set-install-location":
+ return runSetInstallLocation();
+ case "get-install-location":
+ return runGetInstallLocation();
+ case "move-package":
+ return runMovePackage();
+ case "move-primary-storage":
+ return runMovePrimaryStorage();
case "compile":
return runCompile();
case "reconcile-secondary-dex-files":
return runreconcileSecondaryDexFiles();
+ case "force-dex-opt":
+ return runForceDexOpt();
case "bg-dexopt-job":
return runDexoptJob();
case "dump-profiles":
return runDumpProfiles();
- case "list":
- return runList();
case "uninstall":
return runUninstall();
- case "resolve-activity":
- return runResolveActivity();
- case "query-activities":
- return runQueryIntentActivities();
- case "query-services":
- return runQueryIntentServices();
- case "query-receivers":
- return runQueryIntentReceivers();
+ case "clear":
+ return runClear();
+ case "enable":
+ return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ case "disable":
+ return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+ case "disable-user":
+ return runSetEnabledSetting(
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
+ case "disable-until-used":
+ return runSetEnabledSetting(
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ case "default-state":
+ return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+ case "hide":
+ return runSetHiddenSetting(true);
+ case "unhide":
+ return runSetHiddenSetting(false);
case "suspend":
return runSuspend(true);
case "unsuspend":
return runSuspend(false);
- case "set-home-activity":
- return runSetHomeActivity();
+ case "grant":
+ return runGrantRevokePermission(true);
+ case "revoke":
+ return runGrantRevokePermission(false);
+ case "reset-permissions":
+ return runResetPermissions();
+ case "set-permission-enforced":
+ return runSetPermissionEnforced();
case "get-privapp-permissions":
return runGetPrivappPermissions();
case "get-privapp-deny-permissions":
return runGetPrivappDenyPermissions();
case "get-oem-permissions":
return runGetOemPermissions();
+ case "set-app-link":
+ return runSetAppLink();
+ case "get-app-link":
+ return runGetAppLink();
+ case "trim-caches":
+ return runTrimCaches();
+ case "create-user":
+ return runCreateUser();
+ case "remove-user":
+ return runRemoveUser();
+ case "set-user-restriction":
+ return runSetUserRestriction();
+ case "get-max-users":
+ return runGetMaxUsers();
+ case "set-home-activity":
+ return runSetHomeActivity();
+ case "set-installer":
+ return runSetInstaller();
case "get-instantapp-resolver":
return runGetInstantAppResolver();
case "has-feature":
return runHasFeature();
- default:
+ default: {
+ String nextArg = getNextArg();
+ if (nextArg == null) {
+ if (cmd.equalsIgnoreCase("-l")) {
+ return runListPackages(false);
+ } else if (cmd.equalsIgnoreCase("-lf")) {
+ return runListPackages(true);
+ }
+ } else if (getNextArg() == null) {
+ if (cmd.equalsIgnoreCase("-p")) {
+ return displayPackageFilePath(nextArg, UserHandle.USER_SYSTEM);
+ }
+ }
return handleDefaultCommands(cmd);
+ }
}
} catch (RemoteException e) {
pw.println("Remote exception: " + e);
@@ -195,346 +282,40 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
}
-
- private int runInstall() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final InstallParams params = makeInstallParams();
- final String inPath = getNextArg();
-
- setParamsSize(params, inPath);
- final int sessionId = doCreateSession(params.sessionParams,
- params.installerPackageName, params.userId);
- boolean abandonSession = true;
- try {
- if (inPath == null && params.sessionParams.sizeBytes == -1) {
- pw.println("Error: must either specify a package size or an APK file");
- return 1;
- }
- if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
- false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
- return 1;
- }
- if (doCommitSession(sessionId, false /*logSuccess*/)
- != PackageInstaller.STATUS_SUCCESS) {
- return 1;
- }
- abandonSession = false;
- pw.println("Success");
- return 0;
- } finally {
- if (abandonSession) {
- try {
- doAbandonSession(sessionId, false /*logSuccess*/);
- } catch (Exception ignore) {
+ /**
+ * Displays the package file for a package.
+ * @param pckg
+ */
+ private int displayPackageFilePath(String pckg, int userId) throws RemoteException {
+ PackageInfo info = mInterface.getPackageInfo(pckg, 0, userId);
+ if (info != null && info.applicationInfo != null) {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.print("package:");
+ pw.println(info.applicationInfo.sourceDir);
+ if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
+ for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
+ pw.print("package:");
+ pw.println(splitSourceDir);
}
}
- }
- }
-
- private int runSuspend(boolean suspendedState) {
- final PrintWriter pw = getOutPrintWriter();
- int userId = UserHandle.USER_SYSTEM;
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "--user":
- userId = UserHandle.parseUserArg(getNextArgRequired());
- break;
- default:
- pw.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- String packageName = getNextArg();
- if (packageName == null) {
- pw.println("Error: package name not specified");
- return 1;
- }
-
- try {
- mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
- userId);
- pw.println("Package " + packageName + " new suspended state: "
- + mInterface.isPackageSuspendedForUser(packageName, userId));
return 0;
- } catch (RemoteException | IllegalArgumentException e) {
- pw.println(e.toString());
- return 1;
}
+ return 1;
}
- private int runInstallAbandon() throws RemoteException {
- final int sessionId = Integer.parseInt(getNextArg());
- return doAbandonSession(sessionId, true /*logSuccess*/);
- }
-
- private int runInstallCommit() throws RemoteException {
- final int sessionId = Integer.parseInt(getNextArg());
- return doCommitSession(sessionId, true /*logSuccess*/);
- }
-
- private int runInstallCreate() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final InstallParams installParams = makeInstallParams();
- final int sessionId = doCreateSession(installParams.sessionParams,
- installParams.installerPackageName, installParams.userId);
-
- // NOTE: adb depends on parsing this string
- pw.println("Success: created install session [" + sessionId + "]");
- return 0;
- }
-
- private int runInstallWrite() throws RemoteException {
- long sizeBytes = -1;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- if (opt.equals("-S")) {
- sizeBytes = Long.parseLong(getNextArg());
- } else {
- throw new IllegalArgumentException("Unknown option: " + opt);
- }
- }
-
- final int sessionId = Integer.parseInt(getNextArg());
- final String splitName = getNextArg();
- final String path = getNextArg();
- return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
- }
-
- private int runInstallRemove() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
-
- final int sessionId = Integer.parseInt(getNextArg());
-
- final String splitName = getNextArg();
- if (splitName == null) {
- pw.println("Error: split name not specified");
- return 1;
- }
- return doRemoveSplit(sessionId, splitName, true /*logSuccess*/);
- }
-
- private int runInstallExisting() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
+ private int runPath() throws RemoteException {
int userId = UserHandle.USER_SYSTEM;
- int installFlags = 0;
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "--user":
- userId = UserHandle.parseUserArg(getNextArgRequired());
- break;
- case "--ephemeral":
- case "--instant":
- installFlags |= PackageManager.INSTALL_INSTANT_APP;
- installFlags &= ~PackageManager.INSTALL_FULL_APP;
- break;
- case "--full":
- installFlags &= ~PackageManager.INSTALL_INSTANT_APP;
- installFlags |= PackageManager.INSTALL_FULL_APP;
- break;
- default:
- pw.println("Error: Unknown option: " + opt);
- return 1;
- }
+ String option = getNextOption();
+ if (option != null && option.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
}
- final String packageName = getNextArg();
- if (packageName == null) {
- pw.println("Error: package name not specified");
- return 1;
- }
-
- try {
- final int res = mInterface.installExistingPackageAsUser(packageName, userId,
- installFlags, PackageManager.INSTALL_REASON_UNKNOWN);
- if (res == PackageManager.INSTALL_FAILED_INVALID_URI) {
- throw new NameNotFoundException("Package " + packageName + " doesn't exist");
- }
- pw.println("Package " + packageName + " installed for user: " + userId);
- return 0;
- } catch (RemoteException | NameNotFoundException e) {
- pw.println(e.toString());
- return 1;
- }
- }
-
- private int runCompile() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- boolean checkProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
- boolean forceCompilation = false;
- boolean allPackages = false;
- boolean clearProfileData = false;
- String compilerFilter = null;
- String compilationReason = null;
- String checkProfilesRaw = null;
- boolean secondaryDex = false;
- String split = null;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "-a":
- allPackages = true;
- break;
- case "-c":
- clearProfileData = true;
- break;
- case "-f":
- forceCompilation = true;
- break;
- case "-m":
- compilerFilter = getNextArgRequired();
- break;
- case "-r":
- compilationReason = getNextArgRequired();
- break;
- case "--check-prof":
- checkProfilesRaw = getNextArgRequired();
- break;
- case "--reset":
- forceCompilation = true;
- clearProfileData = true;
- compilationReason = "install";
- break;
- case "--secondary-dex":
- secondaryDex = true;
- break;
- case "--split":
- split = getNextArgRequired();
- break;
- default:
- pw.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- if (checkProfilesRaw != null) {
- if ("true".equals(checkProfilesRaw)) {
- checkProfiles = true;
- } else if ("false".equals(checkProfilesRaw)) {
- checkProfiles = false;
- } else {
- pw.println("Invalid value for \"--check-prof\". Expected \"true\" or \"false\".");
- return 1;
- }
- }
-
- if (compilerFilter != null && compilationReason != null) {
- pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " +
- "at the same time");
- return 1;
- }
- if (compilerFilter == null && compilationReason == null) {
- pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " +
- "reason (\"-r\") at the same time");
- return 1;
- }
-
- if (allPackages && split != null) {
- pw.println("-a cannot be specified together with --split");
- return 1;
- }
-
- if (secondaryDex && split != null) {
- pw.println("--secondary-dex cannot be specified together with --split");
- return 1;
- }
-
- String targetCompilerFilter;
- if (compilerFilter != null) {
- if (!DexFile.isValidCompilerFilter(compilerFilter)) {
- pw.println("Error: \"" + compilerFilter +
- "\" is not a valid compilation filter.");
- return 1;
- }
- targetCompilerFilter = compilerFilter;
- } else {
- int reason = -1;
- for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
- if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals(
- compilationReason)) {
- reason = i;
- break;
- }
- }
- if (reason == -1) {
- pw.println("Error: Unknown compilation reason: " + compilationReason);
- return 1;
- }
- targetCompilerFilter =
- PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason);
- }
-
-
- List<String> packageNames = null;
- if (allPackages) {
- packageNames = mInterface.getAllPackages();
- } else {
- String packageName = getNextArg();
- if (packageName == null) {
- pw.println("Error: package name not specified");
- return 1;
- }
- packageNames = Collections.singletonList(packageName);
- }
-
- List<String> failedPackages = new ArrayList<>();
- for (String packageName : packageNames) {
- if (clearProfileData) {
- mInterface.clearApplicationProfileData(packageName);
- }
-
- boolean result = secondaryDex
- ? mInterface.performDexOptSecondary(packageName,
- targetCompilerFilter, forceCompilation)
- : mInterface.performDexOptMode(packageName,
- checkProfiles, targetCompilerFilter, forceCompilation,
- true /* bootComplete */, split);
- if (!result) {
- failedPackages.add(packageName);
- }
- }
-
- if (failedPackages.isEmpty()) {
- pw.println("Success");
- return 0;
- } else if (failedPackages.size() == 1) {
- pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled");
- return 1;
- } else {
- pw.print("Failure: the following packages could not be compiled: ");
- boolean is_first = true;
- for (String packageName : failedPackages) {
- if (is_first) {
- is_first = false;
- } else {
- pw.print(", ");
- }
- pw.print(packageName);
- }
- pw.println();
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
return 1;
}
- }
-
- private int runreconcileSecondaryDexFiles() throws RemoteException {
- String packageName = getNextArg();
- mInterface.reconcileSecondaryDexFiles(packageName);
- return 0;
- }
-
- private int runDexoptJob() throws RemoteException {
- boolean result = mInterface.runBackgroundDexoptJob();
- return result ? 0 : -1;
- }
-
- private int runDumpProfiles() throws RemoteException {
- String packageName = getNextArg();
- mInterface.dumpProfiles(packageName);
- return 0;
+ return displayPackageFilePath(pkg, userId);
}
private int runList() throws RemoteException {
@@ -558,6 +339,11 @@ class PackageManagerShellCommand extends ShellCommand {
return runListPermissionGroups();
case "permissions":
return runListPermissions();
+ case "users":
+ ServiceManager.getService("user").shellCommand(
+ getInFileDescriptor(), getOutFileDescriptor(), getErrFileDescriptor(),
+ new String[] { "list" }, getShellCallback(), adoptResultReceiver());
+ return 0;
}
pw.println("Error: unknown list type '" + type + "'");
return -1;
@@ -590,7 +376,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println();
} else {
pw.println("reqGlEsVersion=0x"
- + Integer.toHexString(fi.reqGlEsVersion));
+ + Integer.toHexString(fi.reqGlEsVersion));
}
}
return 0;
@@ -872,111 +658,6 @@ class PackageManagerShellCommand extends ShellCommand {
return 0;
}
- private int runUninstall() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- int flags = 0;
- int userId = UserHandle.USER_ALL;
- int versionCode = PackageManager.VERSION_CODE_HIGHEST;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "-k":
- flags |= PackageManager.DELETE_KEEP_DATA;
- break;
- case "--user":
- userId = UserHandle.parseUserArg(getNextArgRequired());
- break;
- case "--versionCode":
- versionCode = Integer.parseInt(getNextArgRequired());
- break;
- default:
- pw.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- final String packageName = getNextArg();
- if (packageName == null) {
- pw.println("Error: package name not specified");
- return 1;
- }
-
- // if a split is specified, just remove it and not the whole package
- final String splitName = getNextArg();
- if (splitName != null) {
- return runRemoveSplit(packageName, splitName);
- }
-
- userId = translateUserId(userId, "runUninstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- flags |= PackageManager.DELETE_ALL_USERS;
- } else {
- final PackageInfo info = mInterface.getPackageInfo(packageName,
- PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
- if (info == null) {
- pw.println("Failure [not installed for " + userId + "]");
- return 1;
- }
- final boolean isSystem =
- (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // If we are being asked to delete a system app for just one
- // user set flag so it disables rather than reverting to system
- // version of the app.
- if (isSystem) {
- flags |= PackageManager.DELETE_SYSTEM_APP;
- }
- }
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
- versionCode), null /*callerPackageName*/, flags,
- receiver.getIntentSender(), userId);
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- pw.println("Success");
- return 0;
- } else {
- pw.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- }
-
- private int runRemoveSplit(String packageName, String splitName) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
- sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- sessionParams.appPackageName = packageName;
- final int sessionId =
- doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL);
- boolean abandonSession = true;
- try {
- if (doRemoveSplit(sessionId, splitName, false /*logSuccess*/)
- != PackageInstaller.STATUS_SUCCESS) {
- return 1;
- }
- if (doCommitSession(sessionId, false /*logSuccess*/)
- != PackageInstaller.STATUS_SUCCESS) {
- return 1;
- }
- abandonSession = false;
- pw.println("Success");
- return 0;
- } finally {
- if (abandonSession) {
- try {
- doAbandonSession(sessionId, false /*logSuccess*/);
- } catch (Exception ignore) {
- }
- }
- }
- }
-
private Intent parseIntentAndUser() throws URISyntaxException {
mTargetUser = UserHandle.USER_CURRENT;
mBrief = false;
@@ -1154,6 +835,1029 @@ class PackageManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runInstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams params = makeInstallParams();
+ final String inPath = getNextArg();
+
+ setParamsSize(params, inPath);
+ final int sessionId = doCreateSession(params.sessionParams,
+ params.installerPackageName, params.userId);
+ boolean abandonSession = true;
+ try {
+ if (inPath == null && params.sessionParams.sizeBytes == -1) {
+ pw.println("Error: must either specify a package size or an APK file");
+ return 1;
+ }
+ if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
+ false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ if (doCommitSession(sessionId, false /*logSuccess*/)
+ != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ abandonSession = false;
+ pw.println("Success");
+ return 0;
+ } finally {
+ if (abandonSession) {
+ try {
+ doAbandonSession(sessionId, false /*logSuccess*/);
+ } catch (Exception ignore) {
+ }
+ }
+ }
+ }
+
+ private int runInstallAbandon() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doAbandonSession(sessionId, true /*logSuccess*/);
+ }
+
+ private int runInstallCommit() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doCommitSession(sessionId, true /*logSuccess*/);
+ }
+
+ private int runInstallCreate() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams installParams = makeInstallParams();
+ final int sessionId = doCreateSession(installParams.sessionParams,
+ installParams.installerPackageName, installParams.userId);
+
+ // NOTE: adb depends on parsing this string
+ pw.println("Success: created install session [" + sessionId + "]");
+ return 0;
+ }
+
+ private int runInstallWrite() throws RemoteException {
+ long sizeBytes = -1;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("-S")) {
+ sizeBytes = Long.parseLong(getNextArg());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+
+ final int sessionId = Integer.parseInt(getNextArg());
+ final String splitName = getNextArg();
+ final String path = getNextArg();
+ return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
+ }
+
+ private int runInstallRemove() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+
+ final int sessionId = Integer.parseInt(getNextArg());
+
+ final String splitName = getNextArg();
+ if (splitName == null) {
+ pw.println("Error: split name not specified");
+ return 1;
+ }
+ return doRemoveSplit(sessionId, splitName, true /*logSuccess*/);
+ }
+
+ private int runInstallExisting() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_SYSTEM;
+ int installFlags = 0;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case "--ephemeral":
+ case "--instant":
+ installFlags |= PackageManager.INSTALL_INSTANT_APP;
+ installFlags &= ~PackageManager.INSTALL_FULL_APP;
+ break;
+ case "--full":
+ installFlags &= ~PackageManager.INSTALL_INSTANT_APP;
+ installFlags |= PackageManager.INSTALL_FULL_APP;
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ final String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+
+ try {
+ final int res = mInterface.installExistingPackageAsUser(packageName, userId,
+ installFlags, PackageManager.INSTALL_REASON_UNKNOWN);
+ if (res == PackageManager.INSTALL_FAILED_INVALID_URI) {
+ throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+ }
+ pw.println("Package " + packageName + " installed for user: " + userId);
+ return 0;
+ } catch (RemoteException | NameNotFoundException e) {
+ pw.println(e.toString());
+ return 1;
+ }
+ }
+
+ private int runSetInstallLocation() throws RemoteException {
+ int loc;
+
+ String arg = getNextArg();
+ if (arg == null) {
+ getErrPrintWriter().println("Error: no install location specified.");
+ return 1;
+ }
+ try {
+ loc = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: install location has to be a number.");
+ return 1;
+ }
+ if (!mInterface.setInstallLocation(loc)) {
+ getErrPrintWriter().println("Error: install location has to be a number.");
+ return 1;
+ }
+ return 0;
+ }
+
+ private int runGetInstallLocation() throws RemoteException {
+ int loc = mInterface.getInstallLocation();
+ String locStr = "invalid";
+ if (loc == PackageHelper.APP_INSTALL_AUTO) {
+ locStr = "auto";
+ } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) {
+ locStr = "internal";
+ } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ locStr = "external";
+ }
+ getOutPrintWriter().println(loc + "[" + locStr + "]");
+ return 0;
+ }
+
+ public int runMovePackage() throws RemoteException {
+ final String packageName = getNextArg();
+ if (packageName == null) {
+ getErrPrintWriter().println("Error: package name not specified");
+ return 1;
+ }
+ String volumeUuid = getNextArg();
+ if ("internal".equals(volumeUuid)) {
+ volumeUuid = null;
+ }
+
+ final int moveId = mInterface.movePackage(packageName, volumeUuid);
+
+ int status = mInterface.getMoveStatus(moveId);
+ while (!PackageManager.isMoveStatusFinished(status)) {
+ SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
+ status = mInterface.getMoveStatus(moveId);
+ }
+
+ if (status == PackageManager.MOVE_SUCCEEDED) {
+ getOutPrintWriter().println("Success");
+ return 0;
+ } else {
+ getErrPrintWriter().println("Failure [" + status + "]");
+ return 1;
+ }
+ }
+
+ public int runMovePrimaryStorage() throws RemoteException {
+ String volumeUuid = getNextArg();
+ if ("internal".equals(volumeUuid)) {
+ volumeUuid = null;
+ }
+
+ final int moveId = mInterface.movePrimaryStorage(volumeUuid);
+
+ int status = mInterface.getMoveStatus(moveId);
+ while (!PackageManager.isMoveStatusFinished(status)) {
+ SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
+ status = mInterface.getMoveStatus(moveId);
+ }
+
+ if (status == PackageManager.MOVE_SUCCEEDED) {
+ getOutPrintWriter().println("Success");
+ return 0;
+ } else {
+ getErrPrintWriter().println("Failure [" + status + "]");
+ return 1;
+ }
+ }
+
+ private int runCompile() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean checkProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+ boolean forceCompilation = false;
+ boolean allPackages = false;
+ boolean clearProfileData = false;
+ String compilerFilter = null;
+ String compilationReason = null;
+ String checkProfilesRaw = null;
+ boolean secondaryDex = false;
+ String split = null;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-a":
+ allPackages = true;
+ break;
+ case "-c":
+ clearProfileData = true;
+ break;
+ case "-f":
+ forceCompilation = true;
+ break;
+ case "-m":
+ compilerFilter = getNextArgRequired();
+ break;
+ case "-r":
+ compilationReason = getNextArgRequired();
+ break;
+ case "--check-prof":
+ checkProfilesRaw = getNextArgRequired();
+ break;
+ case "--reset":
+ forceCompilation = true;
+ clearProfileData = true;
+ compilationReason = "install";
+ break;
+ case "--secondary-dex":
+ secondaryDex = true;
+ break;
+ case "--split":
+ split = getNextArgRequired();
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ if (checkProfilesRaw != null) {
+ if ("true".equals(checkProfilesRaw)) {
+ checkProfiles = true;
+ } else if ("false".equals(checkProfilesRaw)) {
+ checkProfiles = false;
+ } else {
+ pw.println("Invalid value for \"--check-prof\". Expected \"true\" or \"false\".");
+ return 1;
+ }
+ }
+
+ if (compilerFilter != null && compilationReason != null) {
+ pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " +
+ "at the same time");
+ return 1;
+ }
+ if (compilerFilter == null && compilationReason == null) {
+ pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " +
+ "reason (\"-r\") at the same time");
+ return 1;
+ }
+
+ if (allPackages && split != null) {
+ pw.println("-a cannot be specified together with --split");
+ return 1;
+ }
+
+ if (secondaryDex && split != null) {
+ pw.println("--secondary-dex cannot be specified together with --split");
+ return 1;
+ }
+
+ String targetCompilerFilter;
+ if (compilerFilter != null) {
+ if (!DexFile.isValidCompilerFilter(compilerFilter)) {
+ pw.println("Error: \"" + compilerFilter +
+ "\" is not a valid compilation filter.");
+ return 1;
+ }
+ targetCompilerFilter = compilerFilter;
+ } else {
+ int reason = -1;
+ for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
+ if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals(
+ compilationReason)) {
+ reason = i;
+ break;
+ }
+ }
+ if (reason == -1) {
+ pw.println("Error: Unknown compilation reason: " + compilationReason);
+ return 1;
+ }
+ targetCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason);
+ }
+
+
+ List<String> packageNames = null;
+ if (allPackages) {
+ packageNames = mInterface.getAllPackages();
+ } else {
+ String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+ packageNames = Collections.singletonList(packageName);
+ }
+
+ List<String> failedPackages = new ArrayList<>();
+ for (String packageName : packageNames) {
+ if (clearProfileData) {
+ mInterface.clearApplicationProfileData(packageName);
+ }
+
+ boolean result = secondaryDex
+ ? mInterface.performDexOptSecondary(packageName,
+ targetCompilerFilter, forceCompilation)
+ : mInterface.performDexOptMode(packageName,
+ checkProfiles, targetCompilerFilter, forceCompilation,
+ true /* bootComplete */, split);
+ if (!result) {
+ failedPackages.add(packageName);
+ }
+ }
+
+ if (failedPackages.isEmpty()) {
+ pw.println("Success");
+ return 0;
+ } else if (failedPackages.size() == 1) {
+ pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled");
+ return 1;
+ } else {
+ pw.print("Failure: the following packages could not be compiled: ");
+ boolean is_first = true;
+ for (String packageName : failedPackages) {
+ if (is_first) {
+ is_first = false;
+ } else {
+ pw.print(", ");
+ }
+ pw.print(packageName);
+ }
+ pw.println();
+ return 1;
+ }
+ }
+
+ private int runreconcileSecondaryDexFiles() throws RemoteException {
+ String packageName = getNextArg();
+ mInterface.reconcileSecondaryDexFiles(packageName);
+ return 0;
+ }
+
+ public int runForceDexOpt() throws RemoteException {
+ mInterface.forceDexOpt(getNextArgRequired());
+ return 0;
+ }
+
+ private int runDexoptJob() throws RemoteException {
+ boolean result = mInterface.runBackgroundDexoptJob();
+ return result ? 0 : -1;
+ }
+
+ private int runDumpProfiles() throws RemoteException {
+ String packageName = getNextArg();
+ mInterface.dumpProfiles(packageName);
+ return 0;
+ }
+
+ private int runUninstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int flags = 0;
+ int userId = UserHandle.USER_ALL;
+ int versionCode = PackageManager.VERSION_CODE_HIGHEST;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-k":
+ flags |= PackageManager.DELETE_KEEP_DATA;
+ break;
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case "--versionCode":
+ versionCode = Integer.parseInt(getNextArgRequired());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ final String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+
+ // if a split is specified, just remove it and not the whole package
+ final String splitName = getNextArg();
+ if (splitName != null) {
+ return runRemoveSplit(packageName, splitName);
+ }
+
+ userId = translateUserId(userId, "runUninstall");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ flags |= PackageManager.DELETE_ALL_USERS;
+ } else {
+ final PackageInfo info = mInterface.getPackageInfo(packageName,
+ PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
+ if (info == null) {
+ pw.println("Failure [not installed for " + userId + "]");
+ return 1;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ flags |= PackageManager.DELETE_SYSTEM_APP;
+ }
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
+ versionCode), null /*callerPackageName*/, flags,
+ receiver.getIntentSender(), userId);
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return 1;
+ }
+ }
+
+ private int runRemoveSplit(String packageName, String splitName) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
+ sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ sessionParams.appPackageName = packageName;
+ final int sessionId =
+ doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL);
+ boolean abandonSession = true;
+ try {
+ if (doRemoveSplit(sessionId, splitName, false /*logSuccess*/)
+ != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ if (doCommitSession(sessionId, false /*logSuccess*/)
+ != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ abandonSession = false;
+ pw.println("Success");
+ return 0;
+ } finally {
+ if (abandonSession) {
+ try {
+ doAbandonSession(sessionId, false /*logSuccess*/);
+ } catch (Exception ignore) {
+ }
+ }
+ }
+ }
+
+ static class ClearDataObserver extends IPackageDataObserver.Stub {
+ boolean finished;
+ boolean result;
+
+ @Override
+ public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
+ synchronized (this) {
+ finished = true;
+ result = succeeded;
+ notifyAll();
+ }
+ }
+ }
+
+ private int runClear() throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+ String option = getNextOption();
+ if (option != null && option.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ }
+
+ String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return 1;
+ }
+
+ ClearDataObserver obs = new ClearDataObserver();
+ ActivityManager.getService().clearApplicationUserData(pkg, obs, userId);
+ synchronized (obs) {
+ while (!obs.finished) {
+ try {
+ obs.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ if (obs.result) {
+ getOutPrintWriter().println("Success");
+ return 0;
+ } else {
+ getErrPrintWriter().println("Failed");
+ return 1;
+ }
+ }
+
+ private static String enabledSettingToString(int state) {
+ switch (state) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+ return "default";
+ case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+ return "enabled";
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ return "disabled";
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+ return "disabled-user";
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ return "disabled-until-used";
+ }
+ return "unknown";
+ }
+
+ private int runSetEnabledSetting(int state) throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+ String option = getNextOption();
+ if (option != null && option.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ }
+
+ String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package or component specified");
+ return 1;
+ }
+ ComponentName cn = ComponentName.unflattenFromString(pkg);
+ if (cn == null) {
+ mInterface.setApplicationEnabledSetting(pkg, state, 0, userId,
+ "shell:" + android.os.Process.myUid());
+ getOutPrintWriter().println("Package " + pkg + " new state: "
+ + enabledSettingToString(
+ mInterface.getApplicationEnabledSetting(pkg, userId)));
+ return 0;
+ } else {
+ mInterface.setComponentEnabledSetting(cn, state, 0, userId);
+ getOutPrintWriter().println("Component " + cn.toShortString() + " new state: "
+ + enabledSettingToString(
+ mInterface.getComponentEnabledSetting(cn, userId)));
+ return 0;
+ }
+ }
+
+ private int runSetHiddenSetting(boolean state) throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+ String option = getNextOption();
+ if (option != null && option.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ }
+
+ String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package or component specified");
+ return 1;
+ }
+ mInterface.setApplicationHiddenSettingAsUser(pkg, state, userId);
+ getOutPrintWriter().println("Package " + pkg + " new hidden state: "
+ + mInterface.getApplicationHiddenSettingAsUser(pkg, userId));
+ return 0;
+ }
+
+ private int runSuspend(boolean suspendedState) {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_SYSTEM;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+
+ try {
+ mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
+ userId);
+ pw.println("Package " + packageName + " new suspended state: "
+ + mInterface.isPackageSuspendedForUser(packageName, userId));
+ return 0;
+ } catch (RemoteException | IllegalArgumentException e) {
+ pw.println(e.toString());
+ return 1;
+ }
+ }
+
+ private int runGrantRevokePermission(boolean grant) throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt = null;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ }
+ }
+
+ String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return 1;
+ }
+ String perm = getNextArg();
+ if (perm == null) {
+ getErrPrintWriter().println("Error: no permission specified");
+ return 1;
+ }
+
+ if (grant) {
+ mInterface.grantRuntimePermission(pkg, perm, userId);
+ } else {
+ mInterface.revokeRuntimePermission(pkg, perm, userId);
+ }
+ return 0;
+ }
+
+ private int runResetPermissions() throws RemoteException {
+ mInterface.resetRuntimePermissions();
+ return 0;
+ }
+
+ private int runSetPermissionEnforced() throws RemoteException {
+ final String permission = getNextArg();
+ if (permission == null) {
+ getErrPrintWriter().println("Error: no permission specified");
+ return 1;
+ }
+ final String enforcedRaw = getNextArg();
+ if (enforcedRaw == null) {
+ getErrPrintWriter().println("Error: no enforcement specified");
+ return 1;
+ }
+ mInterface.setPermissionEnforced(permission, Boolean.parseBoolean(enforcedRaw));
+ return 0;
+ }
+
+ private int runGetPrivappPermissions() {
+ final String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified.");
+ return 1;
+ }
+ ArraySet<String> privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg);
+ getOutPrintWriter().println(privAppPermissions == null
+ ? "{}" : privAppPermissions.toString());
+ return 0;
+ }
+
+ private int runGetPrivappDenyPermissions() {
+ final String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified.");
+ return 1;
+ }
+ ArraySet<String> privAppDenyPermissions =
+ SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
+ getOutPrintWriter().println(privAppDenyPermissions == null
+ ? "{}" : privAppDenyPermissions.toString());
+ return 0;
+ }
+
+ private int runGetOemPermissions() {
+ final String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified.");
+ return 1;
+ }
+ final Map<String, Boolean> oemPermissions = SystemConfig.getInstance()
+ .getOemPermissions(pkg);
+ if (oemPermissions == null || oemPermissions.isEmpty()) {
+ getOutPrintWriter().println("{}");
+ } else {
+ oemPermissions.forEach((permission, granted) ->
+ getOutPrintWriter().println(permission + " granted:" + granted)
+ );
+ }
+ return 0;
+ }
+
+ private String linkStateToString(int state) {
+ switch (state) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: return "undefined";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
+ }
+ return "Unknown link state: " + state;
+ }
+
+ // pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
+ private int runSetAppLink() throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ // Package name to act on; required
+ final String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified.");
+ return 1;
+ }
+
+ // State to apply; {always|ask|never|undefined}, required
+ final String modeString = getNextArg();
+ if (modeString == null) {
+ getErrPrintWriter().println("Error: no app link state specified.");
+ return 1;
+ }
+
+ final int newMode;
+ switch (modeString.toLowerCase()) {
+ case "undefined":
+ newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ break;
+
+ case "always":
+ newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+ break;
+
+ case "ask":
+ newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+ break;
+
+ case "always-ask":
+ newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+ break;
+
+ case "never":
+ newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+ break;
+
+ default:
+ getErrPrintWriter().println("Error: unknown app link state '" + modeString + "'");
+ return 1;
+ }
+
+ final PackageInfo info = mInterface.getPackageInfo(pkg, 0, userId);
+ if (info == null) {
+ getErrPrintWriter().println("Error: package " + pkg + " not found.");
+ return 1;
+ }
+
+ if ((info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
+ getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
+ return 1;
+ }
+
+ if (!mInterface.updateIntentVerificationStatus(pkg, newMode, userId)) {
+ getErrPrintWriter().println("Error: unable to update app link status for " + pkg);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ // pm get-app-link [--user USER_ID] PACKAGE
+ private int runGetAppLink() throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ // Package name to act on; required
+ final String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified.");
+ return 1;
+ }
+
+ final PackageInfo info = mInterface.getPackageInfo(pkg, 0, userId);
+ if (info == null) {
+ getErrPrintWriter().println("Error: package " + pkg + " not found.");
+ return 1;
+ }
+
+ if ((info.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
+ getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
+ return 1;
+ }
+
+ getOutPrintWriter().println(linkStateToString(
+ mInterface.getIntentVerificationStatus(pkg, userId)));
+
+ return 0;
+ }
+
+ private int runTrimCaches() throws RemoteException {
+ String size = getNextArg();
+ if (size == null) {
+ getErrPrintWriter().println("Error: no size specified");
+ return 1;
+ }
+ long multiplier = 1;
+ int len = size.length();
+ char c = size.charAt(len - 1);
+ if (c < '0' || c > '9') {
+ if (c == 'K' || c == 'k') {
+ multiplier = 1024L;
+ } else if (c == 'M' || c == 'm') {
+ multiplier = 1024L*1024L;
+ } else if (c == 'G' || c == 'g') {
+ multiplier = 1024L*1024L*1024L;
+ } else {
+ getErrPrintWriter().println("Invalid suffix: " + c);
+ return 1;
+ }
+ size = size.substring(0, len-1);
+ }
+ long sizeVal;
+ try {
+ sizeVal = Long.parseLong(size) * multiplier;
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: expected number at: " + size);
+ return 1;
+ }
+ String volumeUuid = getNextArg();
+ if ("internal".equals(volumeUuid)) {
+ volumeUuid = null;
+ }
+ ClearDataObserver obs = new ClearDataObserver();
+ mInterface.freeStorageAndNotify(volumeUuid, sizeVal,
+ StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED, obs);
+ synchronized (obs) {
+ while (!obs.finished) {
+ try {
+ obs.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ return 0;
+ }
+
+ private static boolean isNumber(String s) {
+ try {
+ Integer.parseInt(s);
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ return true;
+ }
+
+ public int runCreateUser() throws RemoteException {
+ String name;
+ int userId = -1;
+ int flags = 0;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if ("--profileOf".equals(opt)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else if ("--managed".equals(opt)) {
+ flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ } else if ("--restricted".equals(opt)) {
+ flags |= UserInfo.FLAG_RESTRICTED;
+ } else if ("--ephemeral".equals(opt)) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ } else if ("--guest".equals(opt)) {
+ flags |= UserInfo.FLAG_GUEST;
+ } else if ("--demo".equals(opt)) {
+ flags |= UserInfo.FLAG_DEMO;
+ } else {
+ getErrPrintWriter().println("Error: unknown option " + opt);
+ return 1;
+ }
+ }
+ String arg = getNextArg();
+ if (arg == null) {
+ getErrPrintWriter().println("Error: no user name specified.");
+ return 1;
+ }
+ name = arg;
+ UserInfo info;
+ IUserManager um = IUserManager.Stub.asInterface(
+ ServiceManager.getService(Context.USER_SERVICE));
+ IAccountManager accm = IAccountManager.Stub.asInterface(
+ ServiceManager.getService(Context.ACCOUNT_SERVICE));
+ if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
+ // In non-split user mode, userId can only be SYSTEM
+ int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
+ info = um.createRestrictedProfile(name, parentUserId);
+ accm.addSharedAccountsFromParentUser(parentUserId, userId,
+ (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
+ } else if (userId < 0) {
+ info = um.createUser(name, flags);
+ } else {
+ info = um.createProfileForUser(name, flags, userId, null);
+ }
+
+ if (info != null) {
+ getOutPrintWriter().println("Success: created user id " + info.id);
+ return 0;
+ } else {
+ getErrPrintWriter().println("Error: couldn't create User.");
+ return 1;
+ }
+ }
+
+ public int runRemoveUser() throws RemoteException {
+ int userId;
+ String arg = getNextArg();
+ if (arg == null) {
+ getErrPrintWriter().println("Error: no user id specified.");
+ return 1;
+ }
+ userId = UserHandle.parseUserArg(arg);
+ IUserManager um = IUserManager.Stub.asInterface(
+ ServiceManager.getService(Context.USER_SERVICE));
+ if (um.removeUser(userId)) {
+ getOutPrintWriter().println("Success: removed user");
+ return 0;
+ } else {
+ getErrPrintWriter().println("Error: couldn't remove user id " + userId);
+ return 1;
+ }
+ }
+
+ public int runSetUserRestriction() throws RemoteException {
+ int userId = UserHandle.USER_SYSTEM;
+ String opt = getNextOption();
+ if (opt != null && "--user".equals(opt)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ }
+
+ String restriction = getNextArg();
+ String arg = getNextArg();
+ boolean value;
+ if ("1".equals(arg)) {
+ value = true;
+ } else if ("0".equals(arg)) {
+ value = false;
+ } else {
+ getErrPrintWriter().println("Error: valid value not specified");
+ return 1;
+ }
+ IUserManager um = IUserManager.Stub.asInterface(
+ ServiceManager.getService(Context.USER_SERVICE));
+ um.setUserRestriction(restriction, value, userId);
+ return 0;
+ }
+
+ public int runGetMaxUsers() {
+ getOutPrintWriter().println("Maximum supported users: "
+ + UserManager.getMaxSupportedUsers());
+ return 0;
+ }
+
private static class InstallParams {
SessionParams sessionParams;
String installerPackageName;
@@ -1287,46 +1991,17 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
- private int runGetPrivappPermissions() {
- final String pkg = getNextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified.");
- return 1;
- }
- ArraySet<String> privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg);
- getOutPrintWriter().println(privAppPermissions == null
- ? "{}" : privAppPermissions.toString());
- return 0;
- }
+ private int runSetInstaller() throws RemoteException {
+ final String targetPackage = getNextArg();
+ final String installerPackageName = getNextArg();
- private int runGetPrivappDenyPermissions() {
- final String pkg = getNextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified.");
+ if (targetPackage == null || installerPackageName == null) {
+ getErrPrintWriter().println("Must provide both target and installer package names");
return 1;
}
- ArraySet<String> privAppDenyPermissions =
- SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
- getOutPrintWriter().println(privAppDenyPermissions == null
- ? "{}" : privAppDenyPermissions.toString());
- return 0;
- }
- private int runGetOemPermissions() {
- final String pkg = getNextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified.");
- return 1;
- }
- final Map<String, Boolean> oemPermissions = SystemConfig.getInstance()
- .getOemPermissions(pkg);
- if (oemPermissions == null || oemPermissions.isEmpty()) {
- getOutPrintWriter().println("{}");
- } else {
- oemPermissions.forEach((permission, granted) ->
- getOutPrintWriter().println(permission + " granted:" + granted)
- );
- }
+ mInterface.setInstallerPackageName(targetPackage, installerPackageName);
+ getOutPrintWriter().println("Success");
return 0;
}
@@ -1367,6 +2042,16 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
+ private int runDump() {
+ String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return 1;
+ }
+ ActivityManager.dumpPackageStateStatic(getOutFileDescriptor(), pkg);
+ return 0;
+ }
+
private static String checkAbiArgument(String abi) {
if (TextUtils.isEmpty(abi)) {
throw new IllegalArgumentException("Missing ABI argument");
@@ -1663,52 +2348,31 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
- pw.println(" compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]");
- pw.println(" [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)");
- pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\".");
- pw.println(" Options:");
- pw.println(" -a: compile all packages");
- pw.println(" -c: clear profile data before compiling");
- pw.println(" -f: force compilation even if not needed");
- pw.println(" -m: select compilation mode");
- pw.println(" MODE is one of the dex2oat compiler filters:");
- pw.println(" assume-verified");
- pw.println(" extract");
- pw.println(" verify");
- pw.println(" quicken");
- pw.println(" space-profile");
- pw.println(" space");
- pw.println(" speed-profile");
- pw.println(" speed");
- pw.println(" everything");
- pw.println(" -r: select compilation reason");
- pw.println(" REASON is one of:");
- for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
- pw.println(" " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
- }
- pw.println(" --reset: restore package to its post-install state");
- pw.println(" --check-prof (true | false): look at profiles when doing dexopt?");
- pw.println(" --secondary-dex: compile app secondary dex files");
- pw.println(" --split SPLIT: compile only the given split name");
- pw.println(" bg-dexopt-job");
- pw.println(" Execute the background optimizations immediately.");
- pw.println(" Note that the command only runs the background optimizer logic. It may");
- pw.println(" overlap with the actual job but the job scheduler will not be able to");
- pw.println(" cancel it. It will also run even if the device is not in the idle");
- pw.println(" maintenance mode.");
+ pw.println(" path [--user USER_ID] PACKAGE");
+ pw.println(" Print the path to the .apk of the given PACKAGE.");
+ pw.println("");
+ pw.println(" dump PACKAGE");
+ pw.println(" Print various system state associated with the given PACKAGE.");
+ pw.println("");
pw.println(" list features");
pw.println(" Prints all features of the system.");
+ pw.println("");
+ pw.println(" has-feature FEATURE_NAME [version]");
+ pw.println(" Prints true and returns exit status 0 when system has a FEATURE_NAME,");
+ pw.println(" otherwise prints false and returns exit status 1");
+ pw.println("");
pw.println(" list instrumentation [-f] [TARGET-PACKAGE]");
pw.println(" Prints all test packages; optionally only those targeting TARGET-PACKAGE");
pw.println(" Options:");
pw.println(" -f: dump the name of the .apk file containing the test package");
+ pw.println("");
pw.println(" list libraries");
pw.println(" Prints all system libraries.");
- pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] "
- + "[--uid UID] [--user USER_ID] [FILTER]");
+ pw.println("");
+ pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
+ pw.println(" [--uid UID] [--user USER_ID] [FILTER]");
pw.println(" Prints all packages; optionally only those whose name contains");
- pw.println(" the text in FILTER.");
- pw.println(" Options:");
+ pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
@@ -1720,42 +2384,216 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" -u: also include uninstalled packages");
pw.println(" --uid UID: filter to only show packages with the given UID");
pw.println(" --user USER_ID: only list packages belonging to the given user");
- pw.println(" reconcile-secondary-dex-files TARGET-PACKAGE");
- pw.println(" Reconciles the package secondary dex files with the generated oat files.");
+ pw.println("");
pw.println(" list permission-groups");
pw.println(" Prints all known permission groups.");
+ pw.println("");
pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]");
- pw.println(" Prints all known permissions; optionally only those in GROUP.");
- pw.println(" Options:");
+ pw.println(" Prints all known permissions; optionally only those in GROUP. Options are:");
pw.println(" -g: organize by group");
pw.println(" -f: print all information");
pw.println(" -s: short summary");
pw.println(" -d: only list dangerous permissions");
pw.println(" -u: list only the permissions users will see");
- pw.println(" dump-profiles TARGET-PACKAGE");
- pw.println(" Dumps method/class profile files to");
- pw.println(" /data/misc/profman/TARGET-PACKAGE.txt");
+ pw.println("");
pw.println(" resolve-activity [--brief] [--components] [--user USER_ID] INTENT");
- pw.println(" Prints the activity that resolves to the given Intent.");
+ pw.println(" Prints the activity that resolves to the given INTENT.");
+ pw.println("");
pw.println(" query-activities [--brief] [--components] [--user USER_ID] INTENT");
- pw.println(" Prints all activities that can handle the given Intent.");
+ pw.println(" Prints all activities that can handle the given INTENT.");
+ pw.println("");
pw.println(" query-services [--brief] [--components] [--user USER_ID] INTENT");
- pw.println(" Prints all services that can handle the given Intent.");
+ pw.println(" Prints all services that can handle the given INTENT.");
+ pw.println("");
pw.println(" query-receivers [--brief] [--components] [--user USER_ID] INTENT");
- pw.println(" Prints all broadcast receivers that can handle the given Intent.");
+ pw.println(" Prints all broadcast receivers that can handle the given INTENT.");
+ pw.println("");
+ pw.println(" install [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
+ pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
+ pw.println(" [--originating-uri URI] [---referrer URI]");
+ pw.println(" [--abi ABI_NAME] [--force-sdk]");
+ pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
+ pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]");
+ pw.println(" Install an application. Must provide the apk data to install, either as a");
+ pw.println(" file path or '-' to read from stdin. Options are:");
+ pw.println(" -l: forward lock application");
+ pw.println(" -r: allow replacement of existing application");
+ pw.println(" -t: allow test packages");
+ pw.println(" -i: specify package name of installer owning the app");
+ pw.println(" -s: install application on sdcard");
+ pw.println(" -f: install application on internal flash");
+ pw.println(" -d: allow version code downgrade (debuggable packages only)");
+ pw.println(" -p: partial application install (new split on top of existing pkg)");
+ pw.println(" -g: grant all runtime permissions");
+ pw.println(" -S: size in bytes of package, required for stdin");
+ pw.println(" --user: install under the given user.");
+ pw.println(" --dont-kill: installing a new feature split, don't kill running app");
+ pw.println(" --originating-uri: set URI where app was downloaded from");
+ pw.println(" --referrer: set URI that instigated the install of the app");
+ pw.println(" --pkg: specify expected package name of app being installed");
+ pw.println(" --abi: override the default ABI of the platform");
+ pw.println(" --instantapp: cause the app to be installed as an ephemeral install app");
+ pw.println(" --full: cause the app to be installed as a non-ephemeral full app");
+ pw.println(" --install-location: force the install location:");
+ pw.println(" 0=auto, 1=internal only, 2=prefer external");
+ pw.println(" --force-uuid: force install on to disk volume with given UUID");
+ pw.println(" --force-sdk: allow install even when existing app targets platform");
+ pw.println(" codename but new one targets a final API level");
+ pw.println("");
+ pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
+ pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
+ pw.println(" [--originating-uri URI] [---referrer URI]");
+ pw.println(" [--abi ABI_NAME] [--force-sdk]");
+ pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
+ pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
+ pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
+ pw.println(" to push data into the session, and \"install-commit\" to finish.");
+ pw.println("");
+ pw.println(" install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH|-]");
+ pw.println(" Write an apk into the given install session. If the path is '-', data");
+ pw.println(" will be read from stdin. Options are:");
+ pw.println(" -S: size in bytes of package, required for stdin");
+ pw.println("");
+ pw.println(" install-commit SESSION_ID");
+ pw.println(" Commit the given active install session, installing the app.");
+ pw.println("");
+ pw.println(" install-abandon SESSION_ID");
+ pw.println(" Delete the given active install session.");
+ pw.println("");
+ pw.println(" set-install-location LOCATION");
+ pw.println(" Changes the default install location. NOTE this is only intended for debugging;");
+ pw.println(" using this can cause applications to break and other undersireable behavior.");
+ pw.println(" LOCATION is one of:");
+ pw.println(" 0 [auto]: Let system decide the best location");
+ pw.println(" 1 [internal]: Install on internal device storage");
+ pw.println(" 2 [external]: Install on external media");
+ pw.println("");
+ pw.println(" get-install-location");
+ pw.println(" Returns the current install location: 0, 1 or 2 as per set-install-location.");
+ pw.println("");
+ pw.println(" move-package PACKAGE [internal|UUID]");
+ pw.println("");
+ pw.println(" move-primary-storage [internal|UUID]");
+ pw.println("");
+ pw.println(" pm uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE] PACKAGE [SPLIT]");
+ pw.println(" Remove the given package name from the system. May remove an entire app");
+ pw.println(" if no SPLIT name is specified, otherwise will remove only the split of the");
+ pw.println(" given app. Options are:");
+ pw.println(" -k: keep the data and cache directories around after package removal.");
+ pw.println(" --user: remove the app from the given user.");
+ pw.println(" --versionCode: only uninstall if the app has the given version code.");
+ pw.println("");
+ pw.println(" clear [--user USER_ID] PACKAGE");
+ pw.println(" Deletes all data associated with a package.");
+ pw.println("");
+ pw.println(" enable [--user USER_ID] PACKAGE_OR_COMPONENT");
+ pw.println(" disable [--user USER_ID] PACKAGE_OR_COMPONENT");
+ pw.println(" disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
+ pw.println(" disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
+ pw.println(" default-state [--user USER_ID] PACKAGE_OR_COMPONENT");
+ pw.println(" These commands change the enabled state of a given package or");
+ pw.println(" component (written as \"package/class\").");
+ pw.println("");
+ pw.println(" hide [--user USER_ID] PACKAGE_OR_COMPONENT");
+ pw.println(" unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
+ pw.println("");
pw.println(" suspend [--user USER_ID] TARGET-PACKAGE");
pw.println(" Suspends the specified package (as user).");
+ pw.println("");
pw.println(" unsuspend [--user USER_ID] TARGET-PACKAGE");
pw.println(" Unsuspends the specified package (as user).");
- pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT");
- pw.println(" set the default home activity (aka launcher).");
- pw.println(" has-feature FEATURE_NAME [version]");
- pw.println(" prints true and returns exit status 0 when system has a FEATURE_NAME,");
- pw.println(" otherwise prints false and returns exit status 1");
- pw.println(" get-privileged-permissions TARGET-PACKAGE");
- pw.println(" prints all privileged permissions for a package.");
+ pw.println("");
+ pw.println(" grant [--user USER_ID] PACKAGE PERMISSION");
+ pw.println(" revoke [--user USER_ID] PACKAGE PERMISSION");
+ pw.println(" These commands either grant or revoke permissions to apps. The permissions");
+ pw.println(" must be declared as used in the app's manifest, be runtime permissions");
+ pw.println(" (protection level dangerous), and the app targeting SDK greater than Lollipop MR1.");
+ pw.println("");
+ pw.println(" reset-permissions");
+ pw.println(" Revert all runtime permissions to their default state.");
+ pw.println("");
+ pw.println(" set-permission-enforced PERMISSION [true|false]");
+ pw.println("");
+ pw.println(" get-privapp-permissions TARGET-PACKAGE");
+ pw.println(" Prints all privileged permissions for a package.");
+ pw.println("");
+ pw.println(" get-privapp-deny-permissions TARGET-PACKAGE");
+ pw.println(" Prints all privileged permissions that are denied for a package.");
+ pw.println("");
pw.println(" get-oem-permissions TARGET-PACKAGE");
- pw.println(" prints all OEM permissions for a package.");
+ pw.println(" Prints all OEM permissions for a package.");
+ pw.println("");
+ pw.println(" set-app-link [--user USER_ID] PACKAGE {always|ask|never|undefined}");
+ pw.println(" get-app-link [--user USER_ID] PACKAGE");
+ pw.println("");
+ pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]");
+ pw.println(" Trim cache files to reach the given free space.");
+ pw.println("");
+ pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
+ pw.println(" [--guest] USER_NAME");
+ pw.println(" Create a new user with the given USER_NAME, printing the new user identifier");
+ pw.println(" of the user.");
+ pw.println("");
+ pw.println(" remove-user USER_ID");
+ pw.println(" Remove the user with the given USER_IDENTIFIER, deleting all data");
+ pw.println(" associated with that user");
+ pw.println("");
+ pw.println(" set-user-restriction [--user USER_ID] RESTRICTION VALUE");
+ pw.println("");
+ pw.println(" get-max-users");
+ pw.println("");
+ pw.println(" compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]");
+ pw.println(" [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)");
+ pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\". Options are:");
+ pw.println(" -a: compile all packages");
+ pw.println(" -c: clear profile data before compiling");
+ pw.println(" -f: force compilation even if not needed");
+ pw.println(" -m: select compilation mode");
+ pw.println(" MODE is one of the dex2oat compiler filters:");
+ pw.println(" assume-verified");
+ pw.println(" extract");
+ pw.println(" verify");
+ pw.println(" quicken");
+ pw.println(" space-profile");
+ pw.println(" space");
+ pw.println(" speed-profile");
+ pw.println(" speed");
+ pw.println(" everything");
+ pw.println(" -r: select compilation reason");
+ pw.println(" REASON is one of:");
+ for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
+ pw.println(" " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
+ }
+ pw.println(" --reset: restore package to its post-install state");
+ pw.println(" --check-prof (true | false): look at profiles when doing dexopt?");
+ pw.println(" --secondary-dex: compile app secondary dex files");
+ pw.println(" --split SPLIT: compile only the given split name");
+ pw.println("");
+ pw.println(" force-dex-opt PACKAGE");
+ pw.println(" Force immediate execution of dex opt for the given PACKAGE.");
+ pw.println("");
+ pw.println(" bg-dexopt-job");
+ pw.println(" Execute the background optimizations immediately.");
+ pw.println(" Note that the command only runs the background optimizer logic. It may");
+ pw.println(" overlap with the actual job but the job scheduler will not be able to");
+ pw.println(" cancel it. It will also run even if the device is not in the idle");
+ pw.println(" maintenance mode.");
+ pw.println("");
+ pw.println(" reconcile-secondary-dex-files TARGET-PACKAGE");
+ pw.println(" Reconciles the package secondary dex files with the generated oat files.");
+ pw.println("");
+ pw.println(" dump-profiles TARGET-PACKAGE");
+ pw.println(" Dumps method/class profile files to");
+ pw.println(" /data/misc/profman/TARGET-PACKAGE.txt");
+ pw.println("");
+ pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT");
+ pw.println(" Set the default home activity (aka launcher).");
+ pw.println("");
+ pw.println(" set-installer PACKAGE INSTALLER");
+ pw.println(" Set installer package name");
+ pw.println("");
+ pw.println(" get-instantapp-resolver");
+ pw.println(" Return the name of the component that is the current instant app installer.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 83cb2db2e0d7..3b414e9a0dc5 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -86,6 +86,10 @@ public final class PackageSetting extends PackageSettingBase {
return sharedUserId;
}
+ public SharedUserSetting getSharedUser() {
+ return sharedUser;
+ }
+
@Override
public String toString() {
return "PackageSetting{"
@@ -120,6 +124,14 @@ public final class PackageSetting extends PackageSettingBase {
return appId;
}
+ public void setInstallPermissionsFixed(boolean fixed) {
+ installPermissionsFixed = fixed;
+ }
+
+ public boolean areInstallPermissionsFixed() {
+ return installPermissionsFixed;
+ }
+
public boolean isPrivileged() {
return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
@@ -136,6 +148,10 @@ public final class PackageSetting extends PackageSettingBase {
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ public boolean isUpdatedSystem() {
+ return (pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ }
+
@Override
public boolean isSharedUser() {
return sharedUser != null;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index e19e83fcee83..a83876848910 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
+import android.content.pm.Signature;
import android.service.pm.PackageProto;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -57,7 +58,7 @@ public abstract class PackageSettingBase extends SettingBase {
static final int PKG_INSTALL_COMPLETE = 1;
static final int PKG_INSTALL_INCOMPLETE = 0;
- final String name;
+ public final String name;
final String realName;
String parentPackageName;
@@ -231,6 +232,11 @@ public abstract class PackageSettingBase extends SettingBase {
public boolean isSharedUser() {
return false;
}
+
+ public Signature[] getSignatures() {
+ return signatures.mSignatures;
+ }
+
/**
* Makes a shallow copy of the given package settings.
*
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 191b43a66d51..7077fd166987 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -544,7 +544,7 @@ public final class Settings {
}
final PackageSetting dp = mDisabledSysPackages.get(name);
// always make sure the system package code and resource paths dont change
- if (dp == null && p.pkg != null && p.pkg.isSystemApp() && !p.pkg.isUpdatedSystemApp()) {
+ if (dp == null && p.pkg != null && p.pkg.isSystem() && !p.pkg.isUpdatedSystemApp()) {
if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index b8b00af448eb..bfe09b8ecbce 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -132,11 +132,9 @@ class UserDataPreparer {
if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
FileUtils.deleteContentsAndDir(getUserSystemDirectory(userId));
FileUtils.deleteContentsAndDir(getDataSystemDeDirectory(userId));
- FileUtils.deleteContentsAndDir(getDataMiscDeDirectory(userId));
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
FileUtils.deleteContentsAndDir(getDataSystemCeDirectory(userId));
- FileUtils.deleteContentsAndDir(getDataMiscCeDirectory(userId));
}
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 71d3202db853..8c86db64471d 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -32,6 +32,7 @@ import android.annotation.Nullable;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Permission;
import android.content.pm.PermissionInfo;
+import android.content.pm.Signature;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -129,6 +130,9 @@ public final class BasePermission {
public PackageSettingBase getSourcePackageSetting() {
return sourcePackageSetting;
}
+ public Signature[] getSourceSignatures() {
+ return sourcePackageSetting.getSignatures();
+ }
public int getType() {
return type;
}
@@ -277,8 +281,8 @@ public final class BasePermission {
// Allow system apps to redefine non-system permissions
if (bp != null && !Objects.equals(bp.sourcePackageName, p.info.packageName)) {
final boolean currentOwnerIsSystem = (bp.perm != null
- && bp.perm.owner.isSystemApp());
- if (p.owner.isSystemApp()) {
+ && bp.perm.owner.isSystem());
+ if (p.owner.isSystem()) {
if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
// It's a built-in permission and no owner, take ownership now
bp.sourcePackageSetting = pkgSetting;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 161efd38c0f9..533b261998ae 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -985,7 +985,7 @@ public final class DefaultPermissionGrantPolicy {
private PackageParser.Package getSystemPackage(String packageName) {
PackageParser.Package pkg = getPackage(packageName);
- if (pkg != null && pkg.isSystemApp()) {
+ if (pkg != null && pkg.isSystem()) {
return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null;
}
return null;
@@ -1094,7 +1094,7 @@ public final class DefaultPermissionGrantPolicy {
if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
return true;
}
- if (!pkg.isPrivilegedApp()) {
+ if (!pkg.isPrivileged()) {
return false;
}
final PackageParser.Package disabledPkg =
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index 8aac52ae0df7..60c118b58409 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -19,6 +19,7 @@ package com.android.server.pm.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageParser;
+import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.PermissionInfoFlags;
@@ -57,7 +58,7 @@ public abstract class PermissionManagerInternal {
}
public void onInstallPermissionRevoked() {
}
- public void onPermissionUpdated(int userId) {
+ public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
}
public void onPermissionRemoved() {
}
@@ -65,6 +66,10 @@ public abstract class PermissionManagerInternal {
}
}
+ public abstract void systemReady();
+
+ public abstract boolean isPermissionsReviewRequired(PackageParser.Package pkg, int userId);
+
public abstract void grantRuntimePermission(
@NonNull String permName, @NonNull String packageName, boolean overridePolicy,
int callingUid, int userId, @Nullable PermissionCallback callback);
@@ -78,9 +83,12 @@ public abstract class PermissionManagerInternal {
public abstract void revokeRuntimePermission(@NonNull String permName,
@NonNull String packageName, boolean overridePolicy, int callingUid, int userId,
@Nullable PermissionCallback callback);
- public abstract int[] revokeUnusedSharedUserPermissions(@NonNull SharedUserSetting suSetting,
- @NonNull int[] allUserIds);
+ public abstract void updatePermissions(@Nullable String packageName,
+ @Nullable PackageParser.Package pkg, boolean replaceGrant,
+ @NonNull Collection<PackageParser.Package> allPacakges, PermissionCallback callback);
+ public abstract void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated,
+ @NonNull Collection<PackageParser.Package> allPacakges, PermissionCallback callback);
/**
* Add all permissions in the given package.
@@ -89,22 +97,28 @@ public abstract class PermissionManagerInternal {
* the permission settings.
*/
public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+ public abstract void addAllPermissionGroups(@NonNull PackageParser.Package pkg, boolean chatty);
public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
int callingUid, @Nullable PermissionCallback callback);
public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
@Nullable PermissionCallback callback);
- public abstract int updatePermissions(@Nullable String changingPkg,
- @Nullable PackageParser.Package pkgInfo, int flags);
- public abstract int updatePermissionTrees(@Nullable String changingPkg,
- @Nullable PackageParser.Package pkgInfo, int flags);
-
public abstract @Nullable String[] getAppOpPermissionPackages(@NonNull String permName);
public abstract int getPermissionFlags(@NonNull String permName,
@NonNull String packageName, int callingUid, int userId);
/**
+ * Retrieve all of the information we know about a particular group of permissions.
+ */
+ public abstract @Nullable PermissionGroupInfo getPermissionGroupInfo(
+ @NonNull String groupName, int flags, int callingUid);
+ /**
+ * Retrieve all of the known permission groups in the system.
+ */
+ public abstract @Nullable List<PermissionGroupInfo> getAllPermissionGroups(int flags,
+ int callingUid);
+ /**
* Retrieve all of the information we know about a particular permission.
*/
public abstract @Nullable PermissionInfo getPermissionInfo(@NonNull String permName,
@@ -132,6 +146,7 @@ public abstract class PermissionManagerInternal {
public abstract int checkPermission(@NonNull String permName, @NonNull String packageName,
int callingUid, int userId);
+ public abstract int checkUidPermission(String permName, int uid, int callingUid);
/**
* Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
@@ -147,8 +162,5 @@ public abstract class PermissionManagerInternal {
public abstract @NonNull DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy();
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
- public abstract Iterator<BasePermission> getPermissionIteratorTEMP();
public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
- public abstract void putPermissionTEMP(@NonNull String permName,
- @NonNull BasePermission permission);
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d2d857caa240..9e4940908bda 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -18,6 +18,13 @@ package com.android.server.pm.permission;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
+import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import android.Manifest;
import android.annotation.NonNull;
@@ -27,7 +34,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
-import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.PackageParser.Package;
import android.os.Binder;
@@ -35,18 +42,24 @@ import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.RoSystemProperties;
import com.android.internal.util.ArrayUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -58,6 +71,7 @@ import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.ProcessLoggingHandler;
import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.UserManagerService;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionsState.PermissionState;
@@ -69,6 +83,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -107,8 +122,19 @@ public class PermissionManagerService {
Manifest.permission.READ_PHONE_NUMBERS,
Manifest.permission.ANSWER_PHONE_CALLS);
- /** Cap the size of permission trees that 3rd party apps can define */
- private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text
+ /** Permission grant: not grant the permission. */
+ private static final int GRANT_DENIED = 1;
+ /** Permission grant: grant the permission as an install permission. */
+ private static final int GRANT_INSTALL = 2;
+ /** Permission grant: grant the permission as a runtime one. */
+ private static final int GRANT_RUNTIME = 3;
+ /** Permission grant: grant as runtime a permission that was granted as an install time one. */
+ private static final int GRANT_UPGRADE = 4;
+
+ /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
+ private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
+ /** Empty array to avoid allocations */
+ private static final int[] EMPTY_INT_ARRAY = new int[0];
/** Lock to protect internal data access */
private final Object mLock;
@@ -122,13 +148,29 @@ public class PermissionManagerService {
/** Default permission policy to provide proper behaviour out-of-the-box */
private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
- /** Internal storage for permissions and related settings */
- private final PermissionSettings mSettings;
+ /**
+ * Built-in permissions. Read from system configuration files. Mapping is from
+ * UID to permission name.
+ */
+ private final SparseArray<ArraySet<String>> mSystemPermissions;
+
+ /** Built-in group IDs given to all packages. Read from system configuration files. */
+ private final int[] mGlobalGids;
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Context mContext;
+ /** Internal storage for permissions and related settings */
+ @GuardedBy("mLock")
+ private final PermissionSettings mSettings;
+
+ @GuardedBy("mLock")
+ private ArraySet<String> mPrivappPermissionsViolations;
+
+ @GuardedBy("mLock")
+ private boolean mSystemReady;
+
PermissionManagerService(Context context,
@Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
@NonNull Object externalLock) {
@@ -146,6 +188,9 @@ public class PermissionManagerService {
mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
context, mHandlerThread.getLooper(), defaultGrantCallback, this);
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ mSystemPermissions = systemConfig.getSystemPermissions();
+ mGlobalGids = systemConfig.getGlobalGids();
// propagate permission configuration
final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
@@ -230,14 +275,108 @@ public class PermissionManagerService {
return PackageManager.PERMISSION_DENIED;
}
- private PermissionInfo getPermissionInfo(String name, String packageName, int flags,
+ private int checkUidPermission(String permName, int uid, int callingUid) {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final boolean isCallerInstantApp =
+ mPackageManagerInt.getInstantAppPackageName(callingUid) != null;
+ final boolean isUidInstantApp =
+ mPackageManagerInt.getInstantAppPackageName(uid) != null;
+ final int userId = UserHandle.getUserId(uid);
+ if (!mUserManagerInt.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages != null && packages.length > 0) {
+Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages));
+ PackageParser.Package pkg = null;
+ for (String packageName : packages) {
+ pkg = mPackageManagerInt.getPackage(packageName);
+ if (pkg != null) {
+Slog.e(TAG, "TODD: Using packge: " + packageName);
+ break;
+ } else {
+Slog.e(TAG, "TODD: Missing packge: " + packageName);
+ }
+ }
+ if (pkg == null) {
+Slog.e(TAG, "TODD: No package not found; UID: " + uid);
+ return PackageManager.PERMISSION_DENIED;
+ }
+ if (pkg.mSharedUserId != null) {
+ if (isCallerInstantApp) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ } else {
+ if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+ final PermissionsState permissionsState =
+ ((PackageSetting) pkg.mExtras).getPermissionsState();
+ if (permissionsState.hasPermission(permName, userId)) {
+ if (isUidInstantApp) {
+ if (mSettings.isPermissionInstant(permName)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ } else {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
+ if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
+ .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ } else {
+ ArraySet<String> perms = mSystemPermissions.get(uid);
+ if (perms != null) {
+ if (perms.contains(permName)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
+ .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ private PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags,
+ int callingUid) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ synchronized (mLock) {
+ return PackageParser.generatePermissionGroupInfo(
+ mSettings.mPermissionGroups.get(groupName), flags);
+ }
+ }
+
+ private List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) {
+ if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+ synchronized (mLock) {
+ final int N = mSettings.mPermissionGroups.size();
+ final ArrayList<PermissionGroupInfo> out
+ = new ArrayList<PermissionGroupInfo>(N);
+ for (PackageParser.PermissionGroup pg : mSettings.mPermissionGroups.values()) {
+ out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
+ }
+ return out;
+ }
+ }
+
+ private PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
int callingUid) {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
return null;
}
// reader
synchronized (mLock) {
- final BasePermission bp = mSettings.getPermissionLocked(name);
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
if (bp == null) {
return null;
}
@@ -252,14 +391,10 @@ public class PermissionManagerService {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
return null;
}
- // reader
synchronized (mLock) {
- // TODO Uncomment when mPermissionGroups moves to this class
-// if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
-// // This is thrown as NameNotFoundException
-// return null;
-// }
-
+ if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) {
+ return null;
+ }
final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
for (BasePermission bp : mSettings.mPermissions.values()) {
final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
@@ -314,21 +449,21 @@ public class PermissionManagerService {
// Assume by default that we did not install this permission into the system.
p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
- // Now that permission groups have a special meaning, we ignore permission
- // groups for legacy apps to prevent unexpected behavior. In particular,
- // permissions for one app being granted to someone just because they happen
- // to be in a group defined by another app (before this had no implications).
- if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
- p.group = mPackageManagerInt.getPermissionGroupTEMP(p.info.group);
- // Warn for a permission in an unknown group.
- if (PackageManagerService.DEBUG_PERMISSIONS
- && p.info.group != null && p.group == null) {
- Slog.i(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName + " in an unknown group " + p.info.group);
+ synchronized (PermissionManagerService.this.mLock) {
+ // Now that permission groups have a special meaning, we ignore permission
+ // groups for legacy apps to prevent unexpected behavior. In particular,
+ // permissions for one app being granted to someone just because they happen
+ // to be in a group defined by another app (before this had no implications).
+ if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ p.group = mSettings.mPermissionGroups.get(p.info.group);
+ // Warn for a permission in an unknown group.
+ if (DEBUG_PERMISSIONS
+ && p.info.group != null && p.group == null) {
+ Slog.i(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName + " in an unknown group " + p.info.group);
+ }
}
- }
- synchronized (PermissionManagerService.this.mLock) {
if (p.tree) {
final BasePermission bp = BasePermission.createOrUpdate(
mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
@@ -344,6 +479,48 @@ public class PermissionManagerService {
}
}
+ private void addAllPermissionGroups(PackageParser.Package pkg, boolean chatty) {
+ final int N = pkg.permissionGroups.size();
+ StringBuilder r = null;
+ for (int i=0; i<N; i++) {
+ final PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
+ final PackageParser.PermissionGroup cur = mSettings.mPermissionGroups.get(pg.info.name);
+ final String curPackageName = (cur == null) ? null : cur.info.packageName;
+ final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
+ if (cur == null || isPackageUpdate) {
+ mSettings.mPermissionGroups.put(pg.info.name, pg);
+ if (chatty && DEBUG_PACKAGE_SCANNING) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ if (isPackageUpdate) {
+ r.append("UPD:");
+ }
+ r.append(pg.info.name);
+ }
+ } else {
+ Slog.w(TAG, "Permission group " + pg.info.name + " from package "
+ + pg.info.packageName + " ignored: original from "
+ + cur.info.packageName);
+ if (chatty && DEBUG_PACKAGE_SCANNING) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append("DUP:");
+ r.append(pg.info.name);
+ }
+ }
+ }
+ if (r != null && DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, " Permission Groups: " + r);
+ }
+
+ }
+
private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
synchronized (mLock) {
int N = pkg.permissions.size();
@@ -356,7 +533,7 @@ public class PermissionManagerService {
}
if (bp != null && bp.isPermission(p)) {
bp.setPermission(null);
- if (PackageManagerService.DEBUG_REMOVE && chatty) {
+ if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
@@ -374,7 +551,7 @@ public class PermissionManagerService {
}
}
if (r != null) {
- if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
}
N = pkg.requestedPermissions.size();
@@ -392,7 +569,7 @@ public class PermissionManagerService {
}
}
if (r != null) {
- if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
}
}
}
@@ -455,6 +632,579 @@ public class PermissionManagerService {
}
}
+ private void grantPermissions(PackageParser.Package pkg, boolean replace,
+ String packageOfInterest, PermissionCallback callback) {
+ // IMPORTANT: There are two types of permissions: install and runtime.
+ // Install time permissions are granted when the app is installed to
+ // all device users and users added in the future. Runtime permissions
+ // are granted at runtime explicitly to specific users. Normal and signature
+ // protected permissions are install time permissions. Dangerous permissions
+ // are install permissions if the app's target SDK is Lollipop MR1 or older,
+ // otherwise they are runtime permissions. This function does not manage
+ // runtime permissions except for the case an app targeting Lollipop MR1
+ // being upgraded to target a newer SDK, in which case dangerous permissions
+ // are transformed from install time to runtime ones.
+
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps == null) {
+ return;
+ }
+ final boolean isLegacySystemApp = mPackageManagerInt.isLegacySystemApp(pkg);
+
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ PermissionsState origPermissions = permissionsState;
+
+ final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
+
+ boolean runtimePermissionsRevoked = false;
+ int[] updatedUserIds = EMPTY_INT_ARRAY;
+
+ boolean changedInstallPermission = false;
+
+ if (replace) {
+ ps.setInstallPermissionsFixed(false);
+ if (!ps.isSharedUser()) {
+ origPermissions = new PermissionsState(permissionsState);
+ permissionsState.reset();
+ } else {
+ // We need to know only about runtime permission changes since the
+ // calling code always writes the install permissions state but
+ // the runtime ones are written only if changed. The only cases of
+ // changed runtime permissions here are promotion of an install to
+ // runtime and revocation of a runtime from a shared user.
+ synchronized (mLock) {
+ updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
+ ps.getSharedUser(), UserManagerService.getInstance().getUserIds());
+ if (!ArrayUtils.isEmpty(updatedUserIds)) {
+ runtimePermissionsRevoked = true;
+ }
+ }
+ }
+ }
+
+ permissionsState.setGlobalGids(mGlobalGids);
+
+ synchronized (mLock) {
+ final int N = pkg.requestedPermissions.size();
+ for (int i = 0; i < N; i++) {
+ final String permName = pkg.requestedPermissions.get(i);
+ final BasePermission bp = mSettings.getPermissionLocked(permName);
+ final boolean appSupportsRuntimePermissions =
+ pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
+
+ if (DEBUG_INSTALL) {
+ Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
+ }
+
+ if (bp == null || bp.getSourcePackageSetting() == null) {
+ if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Unknown permission " + permName
+ + " in package " + pkg.packageName);
+ }
+ }
+ continue;
+ }
+
+ // Limit ephemeral apps to ephemeral allowed permissions.
+ if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+ + " for package " + pkg.packageName);
+ }
+ continue;
+ }
+
+ if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+ + " for package " + pkg.packageName);
+ }
+ continue;
+ }
+
+ final String perm = bp.getName();
+ boolean allowedSig = false;
+ int grant = GRANT_DENIED;
+
+ // Keep track of app op permissions.
+ if (bp.isAppOp()) {
+ mSettings.addAppOpPackage(perm, pkg.packageName);
+ }
+
+ if (bp.isNormal()) {
+ // For all apps normal permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else if (bp.isRuntime()) {
+ // If a permission review is required for legacy apps we represent
+ // their permissions as always granted runtime ones since we need
+ // to keep the review required permission flag per user while an
+ // install permission's state is shared across all users.
+ if (!appSupportsRuntimePermissions && !mSettings.mPermissionReviewRequired) {
+ // For legacy apps dangerous permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else if (origPermissions.hasInstallPermission(bp.getName())) {
+ // For legacy apps that became modern, install becomes runtime.
+ grant = GRANT_UPGRADE;
+ } else if (isLegacySystemApp) {
+ // For legacy system apps, install becomes runtime.
+ // We cannot check hasInstallPermission() for system apps since those
+ // permissions were granted implicitly and not persisted pre-M.
+ grant = GRANT_UPGRADE;
+ } else {
+ // For modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
+ }
+ } else if (bp.isSignature()) {
+ // For all apps signature permissions are install time ones.
+ allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
+ }
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Granting permission " + perm + " to package " + pkg.packageName);
+ }
+
+ if (grant != GRANT_DENIED) {
+ if (!ps.isSystem() && ps.areInstallPermissionsFixed()) {
+ // If this is an existing, non-system package, then
+ // we can't add any new permissions to it.
+ if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
+ // Except... if this is a permission that was added
+ // to the platform (note: need to only do this when
+ // updating the platform).
+ if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ grant = GRANT_DENIED;
+ }
+ }
+ }
+
+ switch (grant) {
+ case GRANT_INSTALL: {
+ // Revoke this as runtime permission to handle the case of
+ // a runtime permission being downgraded to an install one.
+ // Also in permission review mode we keep dangerous permissions
+ // for legacy apps
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ if (origPermissions.getRuntimePermissionState(
+ perm, userId) != null) {
+ // Revoke the runtime permission and clear the flags.
+ origPermissions.revokeRuntimePermission(bp, userId);
+ origPermissions.updatePermissionFlags(bp, userId,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ // If we revoked a permission permission, we have to write.
+ updatedUserIds = ArrayUtils.appendInt(
+ updatedUserIds, userId);
+ }
+ }
+ // Grant an install permission.
+ if (permissionsState.grantInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedInstallPermission = true;
+ }
+ } break;
+
+ case GRANT_RUNTIME: {
+ // Grant previously granted runtime permissions.
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final PermissionState permissionState = origPermissions
+ .getRuntimePermissionState(perm, userId);
+ int flags = permissionState != null
+ ? permissionState.getFlags() : 0;
+ if (origPermissions.hasRuntimePermission(perm, userId)) {
+ // Don't propagate the permission in a permission review
+ // mode if the former was revoked, i.e. marked to not
+ // propagate on upgrade. Note that in a permission review
+ // mode install permissions are represented as constantly
+ // granted runtime ones since we need to keep a per user
+ // state associated with the permission. Also the revoke
+ // on upgrade flag is no longer applicable and is reset.
+ final boolean revokeOnUpgrade = (flags & PackageManager
+ .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
+ if (revokeOnUpgrade) {
+ flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ // Since we changed the flags, we have to write.
+ updatedUserIds = ArrayUtils.appendInt(
+ updatedUserIds, userId);
+ }
+ if (!mSettings.mPermissionReviewRequired || !revokeOnUpgrade) {
+ if (permissionsState.grantRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ // If we cannot put the permission as it was,
+ // we have to write.
+ updatedUserIds = ArrayUtils.appendInt(
+ updatedUserIds, userId);
+ }
+ }
+
+ // If the app supports runtime permissions no need for a review.
+ if (mSettings.mPermissionReviewRequired
+ && appSupportsRuntimePermissions
+ && (flags & PackageManager
+ .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ // Since we changed the flags, we have to write.
+ updatedUserIds = ArrayUtils.appendInt(
+ updatedUserIds, userId);
+ }
+ } else if (mSettings.mPermissionReviewRequired
+ && !appSupportsRuntimePermissions) {
+ // For legacy apps that need a permission review, every new
+ // runtime permission is granted but it is pending a review.
+ // We also need to review only platform defined runtime
+ // permissions as these are the only ones the platform knows
+ // how to disable the API to simulate revocation as legacy
+ // apps don't expect to run with revoked permissions.
+ if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
+ if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+ // We changed the flags, hence have to write.
+ updatedUserIds = ArrayUtils.appendInt(
+ updatedUserIds, userId);
+ }
+ }
+ if (permissionsState.grantRuntimePermission(bp, userId)
+ != PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ // We changed the permission, hence have to write.
+ updatedUserIds = ArrayUtils.appendInt(
+ updatedUserIds, userId);
+ }
+ }
+ // Propagate the permission flags.
+ permissionsState.updatePermissionFlags(bp, userId, flags, flags);
+ }
+ } break;
+
+ case GRANT_UPGRADE: {
+ // Grant runtime permissions for a previously held install permission.
+ final PermissionState permissionState = origPermissions
+ .getInstallPermissionState(perm);
+ final int flags =
+ (permissionState != null) ? permissionState.getFlags() : 0;
+
+ if (origPermissions.revokeInstallPermission(bp)
+ != PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ // We will be transferring the permission flags, so clear them.
+ origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ changedInstallPermission = true;
+ }
+
+ // If the permission is not to be promoted to runtime we ignore it and
+ // also its other flags as they are not applicable to install permissions.
+ if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
+ for (int userId : currentUserIds) {
+ if (permissionsState.grantRuntimePermission(bp, userId) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ // Transfer the permission flags.
+ permissionsState.updatePermissionFlags(bp, userId,
+ flags, flags);
+ // If we granted the permission, we have to write.
+ updatedUserIds = ArrayUtils.appendInt(
+ updatedUserIds, userId);
+ }
+ }
+ }
+ } break;
+
+ default: {
+ if (packageOfInterest == null
+ || packageOfInterest.equals(pkg.packageName)) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " because it was previously installed without");
+ }
+ }
+ } break;
+ }
+ } else {
+ if (permissionsState.revokeInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ // Also drop the permission flags.
+ permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ PackageManager.MASK_PERMISSION_FLAGS, 0);
+ changedInstallPermission = true;
+ Slog.i(TAG, "Un-granting permission " + perm
+ + " from package " + pkg.packageName
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ + ")");
+ } else if (bp.isAppOp()) {
+ // Don't print warning for app op permissions, since it is fine for them
+ // not to be granted, there is a UI for the user to decide.
+ if (DEBUG_PERMISSIONS
+ && (packageOfInterest == null
+ || packageOfInterest.equals(pkg.packageName))) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ + ")");
+ }
+ }
+ }
+ }
+
+ if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
+ !ps.isSystem() || ps.isUpdatedSystem()) {
+ // This is the first that we have heard about this package, so the
+ // permissions we have now selected are fixed until explicitly
+ // changed.
+ ps.setInstallPermissionsFixed(true);
+ }
+ }
+
+ // Persist the runtime permissions state for users with changes. If permissions
+ // were revoked because no app in the shared user declares them we have to
+ // write synchronously to avoid losing runtime permissions state.
+ if (callback != null) {
+ callback.onPermissionUpdated(updatedUserIds, runtimePermissionsRevoked);
+ }
+ }
+
+ private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
+ boolean allowed = false;
+ final int NP = PackageParser.NEW_PERMISSIONS.length;
+ for (int ip=0; ip<NP; ip++) {
+ final PackageParser.NewPermissionInfo npi
+ = PackageParser.NEW_PERMISSIONS[ip];
+ if (npi.name.equals(perm)
+ && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) {
+ allowed = true;
+ Log.i(TAG, "Auto-granting " + perm + " to old pkg "
+ + pkg.packageName);
+ break;
+ }
+ }
+ return allowed;
+ }
+
+ /**
+ * Determines whether a package is whitelisted for a particular privapp permission.
+ *
+ * <p>Does NOT check whether the package is a privapp, just whether it's whitelisted.
+ *
+ * <p>This handles parent/child apps.
+ */
+ private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) {
+ ArraySet<String> wlPermissions = SystemConfig.getInstance()
+ .getPrivAppPermissions(pkg.packageName);
+ // Let's check if this package is whitelisted...
+ boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm);
+ // If it's not, we'll also tail-recurse to the parent.
+ return whitelisted ||
+ pkg.parentPackage != null && hasPrivappWhitelistEntry(perm, pkg.parentPackage);
+ }
+
+ private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
+ BasePermission bp, PermissionsState origPermissions) {
+ boolean oemPermission = bp.isOEM();
+ boolean privilegedPermission = bp.isPrivileged();
+ boolean privappPermissionsDisable =
+ RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
+ boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
+ boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);
+ if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged()
+ && !platformPackage && platformPermission) {
+ if (!hasPrivappWhitelistEntry(perm, pkg)) {
+ Slog.w(TAG, "Privileged permission " + perm + " for package "
+ + pkg.packageName + " - not in privapp-permissions whitelist");
+ // Only report violations for apps on system image
+ if (!mSystemReady && !pkg.isUpdatedSystemApp()) {
+ // it's only a reportable violation if the permission isn't explicitly denied
+ final ArraySet<String> deniedPermissions = SystemConfig.getInstance()
+ .getPrivAppDenyPermissions(pkg.packageName);
+ final boolean permissionViolation =
+ deniedPermissions == null || !deniedPermissions.contains(perm);
+ if (permissionViolation
+ && RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
+ if (mPrivappPermissionsViolations == null) {
+ mPrivappPermissionsViolations = new ArraySet<>();
+ }
+ mPrivappPermissionsViolations.add(pkg.packageName + ": " + perm);
+ } else {
+ return false;
+ }
+ }
+ if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
+ return false;
+ }
+ }
+ }
+ final String systemPackageName = mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+ final PackageParser.Package systemPackage =
+ mPackageManagerInt.getPackage(systemPackageName);
+ boolean allowed = (PackageManagerService.compareSignatures(
+ bp.getSourceSignatures(), pkg.mSignatures)
+ == PackageManager.SIGNATURE_MATCH)
+ || (PackageManagerService.compareSignatures(
+ systemPackage.mSignatures, pkg.mSignatures)
+ == PackageManager.SIGNATURE_MATCH);
+ if (!allowed && (privilegedPermission || oemPermission)) {
+ if (pkg.isSystem()) {
+ // For updated system applications, a privileged/oem permission
+ // is granted only if it had been defined by the original application.
+ if (pkg.isUpdatedSystemApp()) {
+ final PackageParser.Package disabledPkg =
+ mPackageManagerInt.getDisabledPackage(pkg.packageName);
+ final PackageSetting disabledPs =
+ (disabledPkg != null) ? (PackageSetting) disabledPkg.mExtras : null;
+ if (disabledPs != null
+ && disabledPs.getPermissionsState().hasInstallPermission(perm)) {
+ // If the original was granted this permission, we take
+ // that grant decision as read and propagate it to the
+ // update.
+ if ((privilegedPermission && disabledPs.isPrivileged())
+ || (oemPermission && disabledPs.isOem()
+ && canGrantOemPermission(disabledPs, perm))) {
+ allowed = true;
+ }
+ } else {
+ // The system apk may have been updated with an older
+ // version of the one on the data partition, but which
+ // granted a new system permission that it didn't have
+ // before. In this case we do want to allow the app to
+ // now get the new permission if the ancestral apk is
+ // privileged to get it.
+ if (disabledPs != null && disabledPkg != null
+ && isPackageRequestingPermission(disabledPkg, perm)
+ && ((privilegedPermission && disabledPs.isPrivileged())
+ || (oemPermission && disabledPs.isOem()
+ && canGrantOemPermission(disabledPs, perm)))) {
+ allowed = true;
+ }
+ // Also if a privileged parent package on the system image or any of
+ // its children requested a privileged/oem permission, the updated child
+ // packages can also get the permission.
+ if (pkg.parentPackage != null) {
+ final PackageParser.Package disabledParentPkg = mPackageManagerInt
+ .getDisabledPackage(pkg.parentPackage.packageName);
+ final PackageSetting disabledParentPs = (disabledParentPkg != null)
+ ? (PackageSetting) disabledParentPkg.mExtras : null;
+ if (disabledParentPkg != null
+ && ((privilegedPermission && disabledParentPs.isPrivileged())
+ || (oemPermission && disabledParentPs.isOem()))) {
+ if (isPackageRequestingPermission(disabledParentPkg, perm)
+ && canGrantOemPermission(disabledParentPs, perm)) {
+ allowed = true;
+ } else if (disabledParentPkg.childPackages != null) {
+ for (PackageParser.Package disabledChildPkg
+ : disabledParentPkg.childPackages) {
+ final PackageSetting disabledChildPs =
+ (disabledChildPkg != null)
+ ? (PackageSetting) disabledChildPkg.mExtras
+ : null;
+ if (isPackageRequestingPermission(disabledChildPkg, perm)
+ && canGrantOemPermission(
+ disabledChildPs, perm)) {
+ allowed = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ allowed = (privilegedPermission && pkg.isPrivileged())
+ || (oemPermission && pkg.isOem()
+ && canGrantOemPermission(ps, perm));
+ }
+ }
+ }
+ if (!allowed) {
+ if (!allowed
+ && bp.isPre23()
+ && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ // If this was a previously normal/dangerous permission that got moved
+ // to a system permission as part of the runtime permission redesign, then
+ // we still want to blindly grant it to old apps.
+ allowed = true;
+ }
+ if (!allowed && bp.isInstaller()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))) {
+ // If this permission is to be granted to the system installer and
+ // this app is an installer, then it gets the permission.
+ allowed = true;
+ }
+ if (!allowed && bp.isVerifier()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM))) {
+ // If this permission is to be granted to the system verifier and
+ // this app is a verifier, then it gets the permission.
+ allowed = true;
+ }
+ if (!allowed && bp.isPreInstalled()
+ && pkg.isSystem()) {
+ // Any pre-installed system app is allowed to get this permission.
+ allowed = true;
+ }
+ if (!allowed && bp.isDevelopment()) {
+ // For development permissions, a development permission
+ // is granted only if it was already granted.
+ allowed = origPermissions.hasInstallPermission(perm);
+ }
+ if (!allowed && bp.isSetup()
+ && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM))) {
+ // If this permission is to be granted to the system setup wizard and
+ // this app is a setup wizard, then it gets the permission.
+ allowed = true;
+ }
+ }
+ return allowed;
+ }
+
+ private static boolean canGrantOemPermission(PackageSetting ps, String permission) {
+ if (!ps.isOem()) {
+ return false;
+ }
+ // all oem permissions must explicitly be granted or denied
+ final Boolean granted =
+ SystemConfig.getInstance().getOemPermissions(ps.name).get(permission);
+ if (granted == null) {
+ throw new IllegalStateException("OEM permission" + permission + " requested by package "
+ + ps.name + " must be explicitly declared granted or not");
+ }
+ return Boolean.TRUE == granted;
+ }
+
+ private boolean isPermissionsReviewRequired(PackageParser.Package pkg, int userId) {
+ if (!mSettings.mPermissionReviewRequired) {
+ return false;
+ }
+
+ // Permission review applies only to apps not supporting the new permission model.
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
+ return false;
+ }
+
+ // Legacy apps have the permission and get user consent on launch.
+ if (pkg == null || pkg.mExtras == null) {
+ return false;
+ }
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ return permissionsState.isPermissionReviewRequired(userId);
+ }
+
+ private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
+ final int permCount = pkg.requestedPermissions.size();
+ for (int j = 0; j < permCount; j++) {
+ String requestedPermission = pkg.requestedPermissions.get(j);
+ if (permission.equals(requestedPermission)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
if (pkg.parentPackage == null) {
@@ -744,7 +1494,8 @@ public class PermissionManagerService {
}
}
- private int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, int[] allUserIds) {
+ private int[] revokeUnusedSharedUserPermissionsLocked(
+ SharedUserSetting suSetting, int[] allUserIds) {
// Collect all used permissions in the UID
final ArraySet<String> usedPermissions = new ArraySet<>();
final List<PackageParser.Package> pkgList = suSetting.getPackages();
@@ -845,7 +1596,79 @@ public class PermissionManagerService {
return permissionsState.getPermissionFlags(permName, userId);
}
- private int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) {
+ private static final int UPDATE_PERMISSIONS_ALL = 1<<0;
+ private static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
+ private static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+
+ private void updatePermissions(String packageName, PackageParser.Package pkg,
+ boolean replaceGrant, Collection<PackageParser.Package> allPackages,
+ PermissionCallback callback) {
+ final int flags = (pkg != null ? UPDATE_PERMISSIONS_ALL : 0) |
+ (replaceGrant ? UPDATE_PERMISSIONS_REPLACE_PKG : 0);
+ updatePermissions(
+ packageName, pkg, getVolumeUuidForPackage(pkg), flags, allPackages, callback);
+ if (pkg != null && pkg.childPackages != null) {
+ for (PackageParser.Package childPkg : pkg.childPackages) {
+ updatePermissions(childPkg.packageName, childPkg,
+ getVolumeUuidForPackage(childPkg), flags, allPackages, callback);
+ }
+ }
+ }
+
+ private void updateAllPermissions(String volumeUuid, boolean sdkUpdated,
+ Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
+ final int flags = UPDATE_PERMISSIONS_ALL |
+ (sdkUpdated
+ ? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
+ : 0);
+ updatePermissions(null, null, volumeUuid, flags, allPackages, callback);
+ }
+
+ private void updatePermissions(String changingPkgName, PackageParser.Package changingPkg,
+ String replaceVolumeUuid, int flags, Collection<PackageParser.Package> allPackages,
+ PermissionCallback callback) {
+ // TODO: Most of the methods exposing BasePermission internals [source package name,
+ // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
+ // have package settings, we should make note of it elsewhere [map between
+ // source package name and BasePermission] and cycle through that here. Then we
+ // define a single method on BasePermission that takes a PackageSetting, changing
+ // package name and a package.
+ // NOTE: With this approach, we also don't need to tree trees differently than
+ // normal permissions. Today, we need two separate loops because these BasePermission
+ // objects are stored separately.
+ // Make sure there are no dangling permission trees.
+ flags = updatePermissionTrees(changingPkgName, changingPkg, flags);
+
+ // Make sure all dynamic permissions have been assigned to a package,
+ // and make sure there are no dangling permissions.
+ flags = updatePermissions(changingPkgName, changingPkg, flags);
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
+ // Now update the permissions for all packages, in particular
+ // replace the granted permissions of the system packages.
+ if ((flags & UPDATE_PERMISSIONS_ALL) != 0) {
+ for (PackageParser.Package pkg : allPackages) {
+ if (pkg != changingPkg) {
+ // Only replace for packages on requested volume
+ final String volumeUuid = getVolumeUuidForPackage(pkg);
+ final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
+ && Objects.equals(replaceVolumeUuid, volumeUuid);
+ grantPermissions(pkg, replace, changingPkgName, callback);
+ }
+ }
+ }
+
+ if (changingPkg != null) {
+ // Only replace for packages on requested volume
+ final String volumeUuid = getVolumeUuidForPackage(changingPkg);
+ final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
+ && Objects.equals(replaceVolumeUuid, volumeUuid);
+ grantPermissions(changingPkg, replace, changingPkgName, callback);
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ private int updatePermissions(String packageName, PackageParser.Package pkg, int flags) {
Set<BasePermission> needsUpdate = null;
synchronized (mLock) {
final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
@@ -856,10 +1679,10 @@ public class PermissionManagerService {
}
if (bp.getSourcePackageSetting() != null) {
if (packageName != null && packageName.equals(bp.getSourcePackageName())
- && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+ && (pkg == null || !hasPermission(pkg, bp.getName()))) {
Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+ " from package " + bp.getSourcePackageName());
- flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+ flags |= UPDATE_PERMISSIONS_ALL;
it.remove();
}
continue;
@@ -872,13 +1695,13 @@ public class PermissionManagerService {
}
if (needsUpdate != null) {
for (final BasePermission bp : needsUpdate) {
- final PackageParser.Package pkg =
+ final PackageParser.Package sourcePkg =
mPackageManagerInt.getPackage(bp.getSourcePackageName());
synchronized (mLock) {
- if (pkg != null && pkg.mExtras != null) {
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (sourcePkg != null && sourcePkg.mExtras != null) {
+ final PackageSetting sourcePs = (PackageSetting) sourcePkg.mExtras;
if (bp.getSourcePackageSetting() == null) {
- bp.setSourcePackageSetting(ps);
+ bp.setSourcePackageSetting(sourcePs);
}
continue;
}
@@ -891,7 +1714,7 @@ public class PermissionManagerService {
return flags;
}
- private int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo,
+ private int updatePermissionTrees(String packageName, PackageParser.Package pkg,
int flags) {
Set<BasePermission> needsUpdate = null;
synchronized (mLock) {
@@ -900,10 +1723,10 @@ public class PermissionManagerService {
final BasePermission bp = it.next();
if (bp.getSourcePackageSetting() != null) {
if (packageName != null && packageName.equals(bp.getSourcePackageName())
- && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+ && (pkg == null || !hasPermission(pkg, bp.getName()))) {
Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+ " from package " + bp.getSourcePackageName());
- flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+ flags |= UPDATE_PERMISSIONS_ALL;
it.remove();
}
continue;
@@ -916,13 +1739,13 @@ public class PermissionManagerService {
}
if (needsUpdate != null) {
for (final BasePermission bp : needsUpdate) {
- final PackageParser.Package pkg =
+ final PackageParser.Package sourcePkg =
mPackageManagerInt.getPackage(bp.getSourcePackageName());
synchronized (mLock) {
- if (pkg != null && pkg.mExtras != null) {
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (sourcePkg != null && sourcePkg.mExtras != null) {
+ final PackageSetting sourcePs = (PackageSetting) sourcePkg.mExtras;
if (bp.getSourcePackageSetting() == null) {
- bp.setSourcePackageSetting(ps);
+ bp.setSourcePackageSetting(sourcePs);
}
continue;
}
@@ -985,7 +1808,7 @@ public class PermissionManagerService {
callback.onInstallPermissionUpdated();
} else if (permissionsState.getRuntimePermissionState(permName, userId) != null
|| hadState) {
- callback.onPermissionUpdated(userId);
+ callback.onPermissionUpdated(new int[] { userId }, false);
}
}
}
@@ -1083,6 +1906,29 @@ public class PermissionManagerService {
}
}
+ private void systemReady() {
+ mSystemReady = true;
+ if (mPrivappPermissionsViolations != null) {
+ throw new IllegalStateException("Signature|privileged permissions not in "
+ + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
+ }
+ }
+
+ private static String getVolumeUuidForPackage(PackageParser.Package pkg) {
+ if (pkg == null) {
+ return StorageManager.UUID_PRIVATE_INTERNAL;
+ }
+ if (pkg.isExternal()) {
+ if (TextUtils.isEmpty(pkg.volumeUuid)) {
+ return StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ return pkg.volumeUuid;
+ }
+ } else {
+ return StorageManager.UUID_PRIVATE_INTERNAL;
+ }
+ }
+
private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) {
for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
if (pkgInfo.permissions.get(i).info.name.equals(permName)) {
@@ -1154,10 +2000,22 @@ public class PermissionManagerService {
private class PermissionManagerInternalImpl extends PermissionManagerInternal {
@Override
+ public void systemReady() {
+ PermissionManagerService.this.systemReady();
+ }
+ @Override
+ public boolean isPermissionsReviewRequired(Package pkg, int userId) {
+ return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId);
+ }
+ @Override
public void addAllPermissions(Package pkg, boolean chatty) {
PermissionManagerService.this.addAllPermissions(pkg, chatty);
}
@Override
+ public void addAllPermissionGroups(Package pkg, boolean chatty) {
+ PermissionManagerService.this.addAllPermissionGroups(pkg, chatty);
+ }
+ @Override
public void removeAllPermissions(Package pkg, boolean chatty) {
PermissionManagerService.this.removeAllPermissions(pkg, chatty);
}
@@ -1198,10 +2056,16 @@ public class PermissionManagerService {
overridePolicy, callingUid, userId, callback);
}
@Override
- public int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting,
- int[] allUserIds) {
- return PermissionManagerService.this.revokeUnusedSharedUserPermissions(
- (SharedUserSetting) suSetting, allUserIds);
+ public void updatePermissions(String packageName, Package pkg, boolean replaceGrant,
+ Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
+ PermissionManagerService.this.updatePermissions(
+ packageName, pkg, replaceGrant, allPackages, callback);
+ }
+ @Override
+ public void updateAllPermissions(String volumeUuid, boolean sdkUpdated,
+ Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
+ PermissionManagerService.this.updateAllPermissions(
+ volumeUuid, sdkUpdated, allPackages, callback);
}
@Override
public String[] getAppOpPermissionPackages(String permName) {
@@ -1214,16 +2078,6 @@ public class PermissionManagerService {
callingUid, userId);
}
@Override
- public int updatePermissions(String packageName,
- PackageParser.Package pkgInfo, int flags) {
- return PermissionManagerService.this.updatePermissions(packageName, pkgInfo, flags);
- }
- @Override
- public int updatePermissionTrees(String packageName,
- PackageParser.Package pkgInfo, int flags) {
- return PermissionManagerService.this.updatePermissionTrees(packageName, pkgInfo, flags);
- }
- @Override
public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int callingUid, int userId, PermissionCallback callback) {
PermissionManagerService.this.updatePermissionFlags(
@@ -1252,6 +2106,20 @@ public class PermissionManagerService {
permName, packageName, callingUid, userId);
}
@Override
+ public int checkUidPermission(String permName, int uid, int callingUid) {
+ return PermissionManagerService.this.checkUidPermission(permName, uid, callingUid);
+ }
+ @Override
+ public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags,
+ int callingUid) {
+ return PermissionManagerService.this.getPermissionGroupInfo(
+ groupName, flags, callingUid);
+ }
+ @Override
+ public List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) {
+ return PermissionManagerService.this.getAllPermissionGroups(flags, callingUid);
+ }
+ @Override
public PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
int callingUid) {
return PermissionManagerService.this.getPermissionInfo(
@@ -1276,17 +2144,5 @@ public class PermissionManagerService {
return mSettings.getPermissionLocked(permName);
}
}
- @Override
- public void putPermissionTEMP(String permName, BasePermission permission) {
- synchronized (PermissionManagerService.this.mLock) {
- mSettings.putPermissionLocked(permName, (BasePermission) permission);
- }
- }
- @Override
- public Iterator<BasePermission> getPermissionIteratorTEMP() {
- synchronized (PermissionManagerService.this.mLock) {
- return mSettings.getAllPermissionsLocked().iterator();
- }
- }
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index 7d125c9ebe87..f6c4990c3294 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -19,6 +19,7 @@ package com.android.server.pm.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.PackageParser;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -46,7 +47,8 @@ import java.util.Set;
*/
public class PermissionSettings {
- final boolean mPermissionReviewRequired;
+ public final boolean mPermissionReviewRequired;
+
/**
* All of the permissions known to the system. The mapping is from permission
* name to permission object.
@@ -64,6 +66,14 @@ public class PermissionSettings {
new ArrayMap<String, BasePermission>();
/**
+ * All permisson groups know to the system. The mapping is from permission group
+ * name to permission group object.
+ */
+ @GuardedBy("mLock")
+ final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups =
+ new ArrayMap<String, PackageParser.PermissionGroup>();
+
+ /**
* Set of packages that request a particular app op. The mapping is from permission
* name to package names.
*/
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b917dae2adf8..2494bded1b86 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -58,10 +58,6 @@ import android.os.WorkSource;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
-import android.service.power.PowerServiceDumpProto;
-import android.service.power.PowerServiceSettingsAndConfigurationDumpProto;
-import android.service.power.SuspendBlockerProto;
-import android.service.power.WakeLockProto;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.EventLog;
@@ -620,8 +616,8 @@ public final class PowerManagerService extends SystemService
}
void dumpProto(ProtoOutputStream proto) {
- final long constantsToken = proto.start(PowerServiceDumpProto.CONSTANTS);
- proto.write(PowerServiceDumpProto.ConstantsProto.IS_NO_CACHED_WAKE_LOCKS,
+ final long constantsToken = proto.start(PowerManagerServiceDumpProto.CONSTANTS);
+ proto.write(PowerManagerServiceDumpProto.ConstantsProto.IS_NO_CACHED_WAKE_LOCKS,
NO_CACHED_WAKE_LOCKS);
proto.end(constantsToken);
}
@@ -3396,112 +3392,112 @@ public final class PowerManagerService extends SystemService
synchronized (mLock) {
mConstants.dumpProto(proto);
- proto.write(PowerServiceDumpProto.DIRTY, mDirty);
- proto.write(PowerServiceDumpProto.WAKEFULNESS, mWakefulness);
- proto.write(PowerServiceDumpProto.IS_WAKEFULNESS_CHANGING, mWakefulnessChanging);
- proto.write(PowerServiceDumpProto.IS_POWERED, mIsPowered);
- proto.write(PowerServiceDumpProto.PLUG_TYPE, mPlugType);
- proto.write(PowerServiceDumpProto.BATTERY_LEVEL, mBatteryLevel);
+ proto.write(PowerManagerServiceDumpProto.DIRTY, mDirty);
+ proto.write(PowerManagerServiceDumpProto.WAKEFULNESS, mWakefulness);
+ proto.write(PowerManagerServiceDumpProto.IS_WAKEFULNESS_CHANGING, mWakefulnessChanging);
+ proto.write(PowerManagerServiceDumpProto.IS_POWERED, mIsPowered);
+ proto.write(PowerManagerServiceDumpProto.PLUG_TYPE, mPlugType);
+ proto.write(PowerManagerServiceDumpProto.BATTERY_LEVEL, mBatteryLevel);
proto.write(
- PowerServiceDumpProto.BATTERY_LEVEL_WHEN_DREAM_STARTED,
+ PowerManagerServiceDumpProto.BATTERY_LEVEL_WHEN_DREAM_STARTED,
mBatteryLevelWhenDreamStarted);
- proto.write(PowerServiceDumpProto.DOCK_STATE, mDockState);
- proto.write(PowerServiceDumpProto.IS_STAY_ON, mStayOn);
- proto.write(PowerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
- proto.write(PowerServiceDumpProto.IS_BOOT_COMPLETED, mBootCompleted);
- proto.write(PowerServiceDumpProto.IS_SYSTEM_READY, mSystemReady);
+ proto.write(PowerManagerServiceDumpProto.DOCK_STATE, mDockState);
+ proto.write(PowerManagerServiceDumpProto.IS_STAY_ON, mStayOn);
+ proto.write(PowerManagerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
+ proto.write(PowerManagerServiceDumpProto.IS_BOOT_COMPLETED, mBootCompleted);
+ proto.write(PowerManagerServiceDumpProto.IS_SYSTEM_READY, mSystemReady);
proto.write(
- PowerServiceDumpProto.IS_HAL_AUTO_SUSPEND_MODE_ENABLED,
+ PowerManagerServiceDumpProto.IS_HAL_AUTO_SUSPEND_MODE_ENABLED,
mHalAutoSuspendModeEnabled);
proto.write(
- PowerServiceDumpProto.IS_HAL_AUTO_INTERACTIVE_MODE_ENABLED,
+ PowerManagerServiceDumpProto.IS_HAL_AUTO_INTERACTIVE_MODE_ENABLED,
mHalInteractiveModeEnabled);
- final long activeWakeLocksToken = proto.start(PowerServiceDumpProto.ACTIVE_WAKE_LOCKS);
+ final long activeWakeLocksToken = proto.start(PowerManagerServiceDumpProto.ACTIVE_WAKE_LOCKS);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_CPU,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_CPU,
(mWakeLockSummary & WAKE_LOCK_CPU) != 0);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_BRIGHT,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_BRIGHT,
(mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_DIM,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_SCREEN_DIM,
(mWakeLockSummary & WAKE_LOCK_SCREEN_DIM) != 0);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_BUTTON_BRIGHT,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_BUTTON_BRIGHT,
(mWakeLockSummary & WAKE_LOCK_BUTTON_BRIGHT) != 0);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_PROXIMITY_SCREEN_OFF,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_PROXIMITY_SCREEN_OFF,
(mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_STAY_AWAKE,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_STAY_AWAKE,
(mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_DOZE,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_DOZE,
(mWakeLockSummary & WAKE_LOCK_DOZE) != 0);
proto.write(
- PowerServiceDumpProto.ActiveWakeLocksProto.IS_DRAW,
+ PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_DRAW,
(mWakeLockSummary & WAKE_LOCK_DRAW) != 0);
proto.end(activeWakeLocksToken);
- proto.write(PowerServiceDumpProto.NOTIFY_LONG_SCHEDULED_MS, mNotifyLongScheduled);
- proto.write(PowerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
- proto.write(PowerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
+ proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_SCHEDULED_MS, mNotifyLongScheduled);
+ proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
+ proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
- final long userActivityToken = proto.start(PowerServiceDumpProto.USER_ACTIVITY);
+ final long userActivityToken = proto.start(PowerManagerServiceDumpProto.USER_ACTIVITY);
proto.write(
- PowerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
+ PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
(mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
proto.write(
- PowerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
+ PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
(mUserActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0);
proto.write(
- PowerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
+ PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
(mUserActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
proto.end(userActivityToken);
proto.write(
- PowerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
+ PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
mRequestWaitForNegativeProximity);
- proto.write(PowerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
- proto.write(PowerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
- proto.write(PowerServiceDumpProto.IS_LOW_POWER_MODE_ENABLED, mLowPowerModeEnabled);
- proto.write(PowerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
- proto.write(PowerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
- proto.write(PowerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
+ proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled);
+ proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned);
+ proto.write(PowerManagerServiceDumpProto.IS_LOW_POWER_MODE_ENABLED, mLowPowerModeEnabled);
+ proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow);
+ proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode);
+ proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode);
for (int id : mDeviceIdleWhitelist) {
- proto.write(PowerServiceDumpProto.DEVICE_IDLE_WHITELIST, id);
+ proto.write(PowerManagerServiceDumpProto.DEVICE_IDLE_WHITELIST, id);
}
for (int id : mDeviceIdleTempWhitelist) {
- proto.write(PowerServiceDumpProto.DEVICE_IDLE_TEMP_WHITELIST, id);
+ proto.write(PowerManagerServiceDumpProto.DEVICE_IDLE_TEMP_WHITELIST, id);
}
- proto.write(PowerServiceDumpProto.LAST_WAKE_TIME_MS, mLastWakeTime);
- proto.write(PowerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastSleepTime);
- proto.write(PowerServiceDumpProto.LAST_USER_ACTIVITY_TIME_MS, mLastUserActivityTime);
+ proto.write(PowerManagerServiceDumpProto.LAST_WAKE_TIME_MS, mLastWakeTime);
+ proto.write(PowerManagerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastSleepTime);
+ proto.write(PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_MS, mLastUserActivityTime);
proto.write(
- PowerServiceDumpProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
+ PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
mLastUserActivityTimeNoChangeLights);
proto.write(
- PowerServiceDumpProto.LAST_INTERACTIVE_POWER_HINT_TIME_MS,
+ PowerManagerServiceDumpProto.LAST_INTERACTIVE_POWER_HINT_TIME_MS,
mLastInteractivePowerHintTime);
proto.write(
- PowerServiceDumpProto.LAST_SCREEN_BRIGHTNESS_BOOST_TIME_MS,
+ PowerManagerServiceDumpProto.LAST_SCREEN_BRIGHTNESS_BOOST_TIME_MS,
mLastScreenBrightnessBoostTime);
proto.write(
- PowerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS,
+ PowerManagerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS,
mScreenBrightnessBoostInProgress);
- proto.write(PowerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady);
+ proto.write(PowerManagerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady);
proto.write(
- PowerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER,
+ PowerManagerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER,
mHoldingWakeLockSuspendBlocker);
proto.write(
- PowerServiceDumpProto.IS_HOLDING_DISPLAY_SUSPEND_BLOCKER,
+ PowerManagerServiceDumpProto.IS_HOLDING_DISPLAY_SUSPEND_BLOCKER,
mHoldingDisplaySuspendBlocker);
final long settingsAndConfigurationToken =
- proto.start(PowerServiceDumpProto.SETTINGS_AND_CONFIGURATION);
+ proto.start(PowerManagerServiceDumpProto.SETTINGS_AND_CONFIGURATION);
proto.write(
PowerServiceSettingsAndConfigurationDumpProto
.IS_DECOUPLE_HAL_AUTO_SUSPEND_MODE_FROM_DISPLAY_CONFIG,
@@ -3698,42 +3694,43 @@ public final class PowerManagerService extends SystemService
final int sleepTimeout = getSleepTimeoutLocked();
final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
- proto.write(PowerServiceDumpProto.SLEEP_TIMEOUT_MS, sleepTimeout);
- proto.write(PowerServiceDumpProto.SCREEN_OFF_TIMEOUT_MS, screenOffTimeout);
- proto.write(PowerServiceDumpProto.SCREEN_DIM_DURATION_MS, screenDimDuration);
- proto.write(PowerServiceDumpProto.ARE_UIDS_CHANGING, mUidsChanging);
- proto.write(PowerServiceDumpProto.ARE_UIDS_CHANGED, mUidsChanged);
+ proto.write(PowerManagerServiceDumpProto.SLEEP_TIMEOUT_MS, sleepTimeout);
+ proto.write(PowerManagerServiceDumpProto.SCREEN_OFF_TIMEOUT_MS, screenOffTimeout);
+ proto.write(PowerManagerServiceDumpProto.SCREEN_DIM_DURATION_MS, screenDimDuration);
+ proto.write(PowerManagerServiceDumpProto.ARE_UIDS_CHANGING, mUidsChanging);
+ proto.write(PowerManagerServiceDumpProto.ARE_UIDS_CHANGED, mUidsChanged);
for (int i = 0; i < mUidState.size(); i++) {
final UidState state = mUidState.valueAt(i);
- final long uIDToken = proto.start(PowerServiceDumpProto.UIDS);
+ final long uIDToken = proto.start(PowerManagerServiceDumpProto.UID_STATES);
final int uid = mUidState.keyAt(i);
- proto.write(PowerServiceDumpProto.UidProto.UID, uid);
- proto.write(PowerServiceDumpProto.UidProto.UID_STRING, UserHandle.formatUid(uid));
- proto.write(PowerServiceDumpProto.UidProto.IS_ACTIVE, state.mActive);
- proto.write(PowerServiceDumpProto.UidProto.NUM_WAKE_LOCKS, state.mNumWakeLocks);
+ proto.write(PowerManagerServiceDumpProto.UidStateProto.UID, uid);
+ proto.write(PowerManagerServiceDumpProto.UidStateProto.UID_STRING, UserHandle.formatUid(uid));
+ proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_ACTIVE, state.mActive);
+ proto.write(PowerManagerServiceDumpProto.UidStateProto.NUM_WAKE_LOCKS, state.mNumWakeLocks);
if (state.mProcState == ActivityManager.PROCESS_STATE_UNKNOWN) {
- proto.write(PowerServiceDumpProto.UidProto.IS_PROCESS_STATE_UNKNOWN, true);
+ proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_PROCESS_STATE_UNKNOWN, true);
} else {
- proto.write(PowerServiceDumpProto.UidProto.PROCESS_STATE, state.mProcState);
+ proto.write(PowerManagerServiceDumpProto.UidStateProto.PROCESS_STATE,
+ ActivityManager.processStateAmToProto(state.mProcState));
}
proto.end(uIDToken);
}
- mHandler.getLooper().writeToProto(proto, PowerServiceDumpProto.LOOPER);
+ mHandler.getLooper().writeToProto(proto, PowerManagerServiceDumpProto.LOOPER);
for (WakeLock wl : mWakeLocks) {
- wl.writeToProto(proto, PowerServiceDumpProto.WAKE_LOCKS);
+ wl.writeToProto(proto, PowerManagerServiceDumpProto.WAKE_LOCKS);
}
for (SuspendBlocker sb : mSuspendBlockers) {
- sb.writeToProto(proto, PowerServiceDumpProto.SUSPEND_BLOCKERS);
+ sb.writeToProto(proto, PowerManagerServiceDumpProto.SUSPEND_BLOCKERS);
}
wcd = mWirelessChargerDetector;
}
if (wcd != null) {
- wcd.writeToProto(proto, PowerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
+ wcd.writeToProto(proto, PowerManagerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
}
proto.flush();
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 515fa399007f..755c5f09599c 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -34,8 +34,6 @@ import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioAttributes;
-import android.nfc.INfcAdapter;
-import android.nfc.NfcAdapter;
import android.os.FileUtils;
import android.os.Handler;
import android.os.PowerManager;
@@ -124,7 +122,6 @@ public final class ShutdownThread extends Thread {
private static String METRIC_RADIOS = "shutdown_radios";
private static String METRIC_BT = "shutdown_bt";
private static String METRIC_RADIO = "shutdown_radio";
- private static String METRIC_NFC = "shutdown_nfc";
private static String METRIC_SM = "shutdown_storage_manager";
private final Object mActionDoneSync = new Object();
@@ -629,29 +626,14 @@ public final class ShutdownThread extends Thread {
Thread t = new Thread() {
public void run() {
TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
- boolean nfcOff;
boolean bluetoothReadyForShutdown;
boolean radioOff;
- final INfcAdapter nfc =
- INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
final ITelephony phone =
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
final IBluetoothManager bluetooth =
IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
- try {
- nfcOff = nfc == null ||
- nfc.getState() == NfcAdapter.STATE_OFF;
- if (!nfcOff) {
- Log.w(TAG, "Turning off NFC...");
- metricStarted(METRIC_NFC);
- nfc.disable(false); // Don't persist new state
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during NFC shutdown", ex);
- nfcOff = true;
- }
try {
bluetoothReadyForShutdown = bluetooth == null ||
@@ -678,7 +660,7 @@ public final class ShutdownThread extends Thread {
radioOff = true;
}
- Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
+ Log.i(TAG, "Waiting for Bluetooth and Radio...");
long delay = endTime - SystemClock.elapsedRealtime();
while (delay > 0) {
@@ -722,23 +704,9 @@ public final class ShutdownThread extends Thread {
.logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
}
}
- if (!nfcOff) {
- try {
- nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException during NFC shutdown", ex);
- nfcOff = true;
- }
- if (nfcOff) {
- Log.i(TAG, "NFC turned off.");
- metricEnded(METRIC_NFC);
- shutdownTimingsTraceLog
- .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC));
- }
- }
- if (radioOff && bluetoothReadyForShutdown && nfcOff) {
- Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
+ if (radioOff && bluetoothReadyForShutdown) {
+ Log.i(TAG, "Radio and Bluetooth shutdown complete.");
done[0] = true;
break;
}
@@ -755,7 +723,7 @@ public final class ShutdownThread extends Thread {
} catch (InterruptedException ex) {
}
if (!done[0]) {
- Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
+ Log.w(TAG, "Timed out waiting for Radio and Bluetooth shutdown.");
}
}
diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java
index 6ee9dcd3cfdb..54487e39d82f 100644
--- a/services/core/java/com/android/server/power/WirelessChargerDetector.java
+++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java
@@ -24,7 +24,6 @@ import android.os.BatteryManager;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
-import android.service.power.WirelessChargerDetectorProto;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 52526e2f7d24..5ce4d46c6b4a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -137,6 +137,8 @@ public class DockedStackDividerController implements DimLayerUser {
float mLastDividerProgress;
private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
private boolean mImeHideRequested;
+ private final Rect mLastDimLayerRect = new Rect();
+ private float mLastDimLayerAlpha;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -525,7 +527,6 @@ public class DockedStackDividerController implements DimLayerUser {
* display in that windowing mode.
*/
void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- mService.openSurfaceTransaction();
// TODO: Maybe only allow split-screen windowing modes?
final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
? mDisplayContent.getStack(targetWindowingMode)
@@ -535,16 +536,33 @@ public class DockedStackDividerController implements DimLayerUser {
if (visibleAndValid) {
stack.getDimBounds(mTmpRect);
if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
- mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
+ if (!mLastDimLayerRect.equals(mTmpRect) || mLastDimLayerAlpha != alpha) {
+ try {
+ // TODO: This should use the regular animation transaction - here and below
+ mService.openSurfaceTransaction();
+ mDimLayer.setBounds(mTmpRect);
+ mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
+ } finally {
+ mService.closeSurfaceTransaction();
+ }
+ }
+ mLastDimLayerRect.set(mTmpRect);
+ mLastDimLayerAlpha = alpha;
} else {
visibleAndValid = false;
}
}
if (!visibleAndValid) {
- mDimLayer.hide();
+ if (mLastDimLayerAlpha != 0f) {
+ try {
+ mService.openSurfaceTransaction();
+ mDimLayer.hide();
+ } finally {
+ mService.closeSurfaceTransaction();
+ }
+ }
+ mLastDimLayerAlpha = 0f;
}
- mService.closeSurfaceTransaction();
}
/**
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
new file mode 100644
index 000000000000..36871b1d8c5b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.graphics.PixelFormat.TRANSLUCENT;
+import static android.view.SurfaceControl.HIDDEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.ClipData;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Slog;
+import android.view.Display;
+import android.view.IWindow;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.view.View;
+import com.android.server.wm.WindowManagerService.H;
+
+/**
+ * Managing drag and drop operations initiated by View#startDragAndDrop.
+ */
+class DragDropController {
+ private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
+ private static final long DRAG_TIMEOUT_MS = 5000;
+
+ IBinder prepareDrag(WindowManagerService service, SurfaceSession session, int callerPid,
+ int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
+ + " flags=" + Integer.toHexString(flags) + " win=" + window
+ + " asbinder=" + window.asBinder());
+ }
+
+ IBinder token = null;
+
+ synchronized (service.mWindowMap) {
+ try {
+ if (service.mDragState == null) {
+ // TODO(multi-display): support other displays
+ final DisplayContent displayContent =
+ service.getDefaultDisplayContentLocked();
+ final Display display = displayContent.getDisplay();
+
+ SurfaceControl surface = new SurfaceControl(session, "drag surface",
+ width, height, TRANSLUCENT, HIDDEN);
+ surface.setLayerStack(display.getLayerStack());
+ float alpha = 1;
+ if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
+ alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
+ }
+ surface.setAlpha(alpha);
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG "
+ + surface + ": CREATE");
+ outSurface.copyFrom(surface);
+ final IBinder winBinder = window.asBinder();
+ token = new Binder();
+ service.mDragState =
+ new DragState(service, token, surface, flags, winBinder);
+ service.mDragState.mPid = callerPid;
+ service.mDragState.mUid = callerUid;
+ service.mDragState.mOriginalAlpha = alpha;
+ token = service.mDragState.mToken = new Binder();
+
+ // 5 second timeout for this window to actually begin the drag
+ service.mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
+ Message msg = service.mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
+ service.mH.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
+ } else {
+ Slog.w(TAG_WM, "Drag already in progress");
+ }
+ } catch (OutOfResourcesException e) {
+ Slog.e(TAG_WM, "Can't allocate drag surface w=" + width + " h=" + height,
+ e);
+ if (service.mDragState != null) {
+ service.mDragState.reset();
+ service.mDragState = null;
+ }
+ }
+ }
+
+ return token;
+ }
+
+ boolean performDrag(WindowManagerService service, IWindow window, IBinder dragToken,
+ int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+ ClipData data) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
+ }
+
+ synchronized (service.mWindowMap) {
+ if (service.mDragState == null) {
+ Slog.w(TAG_WM, "No drag prepared");
+ throw new IllegalStateException("performDrag() without prepareDrag()");
+ }
+
+ if (dragToken != service.mDragState.mToken) {
+ Slog.w(TAG_WM, "Performing mismatched drag");
+ throw new IllegalStateException("performDrag() does not match prepareDrag()");
+ }
+
+ final WindowState callingWin = service.windowForClientLocked(null, window, false);
+ if (callingWin == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + window);
+ return false; // !!! TODO: throw here?
+ }
+
+ // !!! TODO: if input is not still focused on the initiating window, fail
+ // the drag initiation (e.g. an alarm window popped up just as the application
+ // called performDrag()
+
+ service.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
+
+ // !!! TODO: extract the current touch (x, y) in screen coordinates. That
+ // will let us eliminate the (touchX,touchY) parameters from the API.
+
+ // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
+ // the actual drag event dispatch stuff in the dragstate
+
+ final DisplayContent displayContent = callingWin.getDisplayContent();
+ if (displayContent == null) {
+ return false;
+ }
+ Display display = displayContent.getDisplay();
+ service.mDragState.register(display);
+ if (!service.mInputManager.transferTouchFocus(callingWin.mInputChannel,
+ service.mDragState.getInputChannel())) {
+ Slog.e(TAG_WM, "Unable to transfer touch focus");
+ service.mDragState.unregister();
+ service.mDragState.reset();
+ service.mDragState = null;
+ return false;
+ }
+
+ service.mDragState.mDisplayContent = displayContent;
+ service.mDragState.mData = data;
+ service.mDragState.broadcastDragStartedLw(touchX, touchY);
+ service.mDragState.overridePointerIconLw(touchSource);
+
+ // remember the thumb offsets for later
+ service.mDragState.mThumbOffsetX = thumbCenterX;
+ service.mDragState.mThumbOffsetY = thumbCenterY;
+
+ // Make the surface visible at the proper location
+ final SurfaceControl surfaceControl = service.mDragState.mSurfaceControl;
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
+ TAG_WM, ">>> OPEN TRANSACTION performDrag");
+ service.openSurfaceTransaction();
+ try {
+ surfaceControl.setPosition(touchX - thumbCenterX,
+ touchY - thumbCenterY);
+ surfaceControl.setLayer(service.mDragState.getDragLayerLw());
+ surfaceControl.setLayerStack(display.getLayerStack());
+ surfaceControl.show();
+ } finally {
+ service.closeSurfaceTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
+ TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+ }
+
+ service.mDragState.notifyLocationLw(touchX, touchY);
+ }
+
+ return true; // success!
+ }
+
+ void reportDropResult(WindowManagerService service, IWindow window, boolean consumed) {
+ IBinder token = window.asBinder();
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
+ }
+
+ synchronized (service.mWindowMap) {
+ if (service.mDragState == null) {
+ // Most likely the drop recipient ANRed and we ended the drag
+ // out from under it. Log the issue and move on.
+ Slog.w(TAG_WM, "Drop result given but no drag in progress");
+ return;
+ }
+
+ if (service.mDragState.mToken != token) {
+ // We're in a drag, but the wrong window has responded.
+ Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
+ throw new IllegalStateException("reportDropResult() by non-recipient");
+ }
+
+ // The right window has responded, even if it's no longer around,
+ // so be sure to halt the timeout even if the later WindowState
+ // lookup fails.
+ service.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
+ WindowState callingWin = service.windowForClientLocked(null, window, false);
+ if (callingWin == null) {
+ Slog.w(TAG_WM, "Bad result-reporting window " + window);
+ return; // !!! TODO: throw here?
+ }
+
+ service.mDragState.mDragResult = consumed;
+ service.mDragState.endDragLw();
+ }
+ }
+
+ void cancelDragAndDrop(WindowManagerService service, IBinder dragToken) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "cancelDragAndDrop");
+ }
+
+ synchronized (service.mWindowMap) {
+ if (service.mDragState == null) {
+ Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
+ throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
+ }
+
+ if (service.mDragState.mToken != dragToken) {
+ Slog.w(TAG_WM,
+ "cancelDragAndDrop() does not match prepareDrag()");
+ throw new IllegalStateException(
+ "cancelDragAndDrop() does not match prepareDrag()");
+ }
+
+ service.mDragState.mDragResult = false;
+ service.mDragState.cancelDragLw();
+ }
+ }
+
+ void dragRecipientEntered(IWindow window) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
+ }
+ }
+
+ void dragRecipientExited(IWindow window) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
+ }
+ }
+
+ void handleMessage(WindowManagerService service, Message msg) {
+ switch (msg.what) {
+ case H.DRAG_START_TIMEOUT: {
+ IBinder win = (IBinder) msg.obj;
+ if (DEBUG_DRAG) {
+ Slog.w(TAG_WM, "Timeout starting drag by win " + win);
+ }
+ synchronized (service.mWindowMap) {
+ // !!! TODO: ANR the app that has failed to start the drag in time
+ if (service.mDragState != null) {
+ service.mDragState.unregister();
+ service.mDragState.reset();
+ service.mDragState = null;
+ }
+ }
+ break;
+ }
+
+ case H.DRAG_END_TIMEOUT: {
+ IBinder win = (IBinder) msg.obj;
+ if (DEBUG_DRAG) {
+ Slog.w(TAG_WM, "Timeout ending drag to win " + win);
+ }
+ synchronized (service.mWindowMap) {
+ // !!! TODO: ANR the drag-receiving app
+ if (service.mDragState != null) {
+ service.mDragState.mDragResult = false;
+ service.mDragState.endDragLw();
+ }
+ }
+ break;
+ }
+
+ case H.TEAR_DOWN_DRAG_AND_DROP_INPUT: {
+ if (DEBUG_DRAG)
+ Slog.d(TAG_WM, "Drag ending; tearing down input channel");
+ DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
+ if (interceptor != null) {
+ synchronized (service.mWindowMap) {
+ interceptor.tearDown();
+ }
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 3fdafc7bebdf..4a75e010add5 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -22,6 +22,10 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.animation.Animator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
@@ -29,7 +33,9 @@ import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.input.InputManager;
import android.os.Build;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -44,16 +50,12 @@ import android.view.InputChannel;
import android.view.InputDevice;
import android.view.PointerIcon;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
-import android.view.animation.TranslateAnimation;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
@@ -78,8 +80,20 @@ class DragState {
View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION |
View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION;
+ // Property names for animations
+ private static final String ANIMATED_PROPERTY_X = "x";
+ private static final String ANIMATED_PROPERTY_Y = "y";
+ private static final String ANIMATED_PROPERTY_ALPHA = "alpha";
+ private static final String ANIMATED_PROPERTY_SCALE = "scale";
+
+ // Messages for Handler.
+ private static final int MSG_ANIMATION_END = 0;
+
final WindowManagerService mService;
IBinder mToken;
+ /**
+ * Do not use the variable from the out of animation thread while mAnimator is not null.
+ */
SurfaceControl mSurfaceControl;
int mFlags;
IBinder mLocalWin;
@@ -101,10 +115,10 @@ class DragState {
boolean mDragInProgress;
DisplayContent mDisplayContent;
- private Animation mAnimation;
- final Transformation mTransformation = new Transformation();
+ @Nullable private ValueAnimator mAnimator;
private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
private Point mDisplaySize = new Point();
+ private final Handler mHandler;
DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
int flags, IBinder localWin) {
@@ -114,9 +128,14 @@ class DragState {
mFlags = flags;
mLocalWin = localWin;
mNotifiedWindows = new ArrayList<WindowState>();
+ mHandler = new DragStateHandler(service.mH.getLooper());
}
void reset() {
+ if (mAnimator != null) {
+ Slog.wtf(TAG_WM,
+ "Unexpectedly destroying mSurfaceControl while animation is running");
+ }
if (mSurfaceControl != null) {
mSurfaceControl.destroy();
}
@@ -388,23 +407,33 @@ class DragState {
}
void endDragLw() {
- if (mAnimation != null) {
+ if (mAnimator != null) {
return;
}
if (!mDragResult) {
- mAnimation = createReturnAnimationLocked();
- mService.scheduleAnimationLocked();
+ mAnimator = createReturnAnimationLocked();
return; // Will call cleanUpDragLw when the animation is done.
}
cleanUpDragLw();
}
void cancelDragLw() {
- if (mAnimation != null) {
+ if (mAnimator != null) {
+ return;
+ }
+ if (!mDragInProgress) {
+ // This can happen if an app invokes Session#cancelDragAndDrop before
+ // Session#performDrag. Reset the drag state:
+ // 1. without sending the end broadcast because the start broadcast has not been sent,
+ // and
+ // 2. without playing the cancel animation because H.DRAG_START_TIMEOUT may be sent to
+ // WindowManagerService, which will cause DragState#reset() while playing the
+ // cancel animation.
+ reset();
+ mService.mDragState = null;
return;
}
- mAnimation = createCancelAnimationLocked();
- mService.scheduleAnimationLocked();
+ mAnimator = createCancelAnimationLocked();
}
private void cleanUpDragLw() {
@@ -422,7 +451,7 @@ class DragState {
}
void notifyMoveLw(float x, float y) {
- if (mAnimation != null) {
+ if (mAnimator != null) {
return;
}
mCurrentX = x;
@@ -491,7 +520,7 @@ class DragState {
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
boolean notifyDropLw(float x, float y) {
- if (mAnimation != null) {
+ if (mAnimator != null) {
return false;
}
mCurrentX = x;
@@ -560,56 +589,52 @@ class DragState {
dragAndDropPermissions, result);
}
- boolean stepAnimationLocked(long currentTimeMs) {
- if (mAnimation == null) {
- return false;
- }
-
- mTransformation.clear();
- if (!mAnimation.getTransformation(currentTimeMs, mTransformation)) {
- cleanUpDragLw();
- return false;
- }
-
- mTransformation.getMatrix().postTranslate(
- mCurrentX - mThumbOffsetX, mCurrentY - mThumbOffsetY);
- final float tmpFloats[] = mService.mTmpFloats;
- mTransformation.getMatrix().getValues(tmpFloats);
- mSurfaceControl.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
- mSurfaceControl.setAlpha(mTransformation.getAlpha());
- mSurfaceControl.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
- tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
- return true;
- }
+ private ValueAnimator createReturnAnimationLocked() {
+ final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX,
+ mOriginalX - mThumbOffsetX),
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY,
+ mOriginalY - mThumbOffsetY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 1),
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2));
- private Animation createReturnAnimationLocked() {
- final AnimationSet set = new AnimationSet(false);
final float translateX = mOriginalX - mCurrentX;
final float translateY = mOriginalY - mCurrentY;
- set.addAnimation(new TranslateAnimation( 0, translateX, 0, translateY));
- set.addAnimation(new AlphaAnimation(mOriginalAlpha, mOriginalAlpha / 2));
// Adjust the duration to the travel distance.
final double travelDistance = Math.sqrt(translateX * translateX + translateY * translateY);
final double displayDiagonal =
Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
final long duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal
* (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
- set.setDuration(duration);
- set.setInterpolator(mCubicEaseOutInterpolator);
- set.initialize(0, 0, 0, 0);
- set.start(); // Will start on the first call to getTransformation.
- return set;
+ final AnimationListener listener = new AnimationListener();
+ animator.setDuration(duration);
+ animator.setInterpolator(mCubicEaseOutInterpolator);
+ animator.addListener(listener);
+ animator.addUpdateListener(listener);
+
+ mService.mAnimationHandler.post(() -> animator.start());
+ return animator;
}
- private Animation createCancelAnimationLocked() {
- final AnimationSet set = new AnimationSet(false);
- set.addAnimation(new ScaleAnimation(1, 0, 1, 0, mThumbOffsetX, mThumbOffsetY));
- set.addAnimation(new AlphaAnimation(mOriginalAlpha, 0));
- set.setDuration(MIN_ANIMATION_DURATION_MS);
- set.setInterpolator(mCubicEaseOutInterpolator);
- set.initialize(0, 0, 0, 0);
- set.start(); // Will start on the first call to getTransformation.
- return set;
+ private ValueAnimator createCancelAnimationLocked() {
+ final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX),
+ PropertyValuesHolder.ofFloat(
+ ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 0),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0));
+ final AnimationListener listener = new AnimationListener();
+ animator.setDuration(MIN_ANIMATION_DURATION_MS);
+ animator.setInterpolator(mCubicEaseOutInterpolator);
+ animator.addListener(listener);
+ animator.addUpdateListener(listener);
+
+ mService.mAnimationHandler.post(() -> animator.start());
+ return animator;
}
private boolean isFromSource(int source) {
@@ -622,4 +647,68 @@ class DragState {
InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_GRABBING);
}
}
+
+ private class DragStateHandler extends Handler {
+ DragStateHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ANIMATION_END:
+ synchronized (mService.mWindowMap) {
+ if (mService.mDragState != DragState.this) {
+ Slog.wtf(TAG_WM, "mDragState is updated unexpectedly while " +
+ "playing animation");
+ return;
+ }
+ if (mAnimator == null) {
+ Slog.wtf(TAG_WM, "Unexpected null mAnimator");
+ return;
+ }
+ mAnimator = null;
+ cleanUpDragLw();
+ }
+ break;
+ }
+ }
+ }
+
+ private class AnimationListener
+ implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ try (final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setPosition(
+ mSurfaceControl,
+ (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_X),
+ (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_Y));
+ transaction.setAlpha(
+ mSurfaceControl,
+ (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_ALPHA));
+ transaction.setMatrix(
+ mSurfaceControl,
+ (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0,
+ 0, (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE));
+ transaction.apply();
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animator) {}
+
+ @Override
+ public void onAnimationCancel(Animator animator) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animator) {}
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ // Updating mDragState requires the WM lock so continues it on the out of
+ // AnimationThread.
+ mHandler.sendEmptyMessage(MSG_ANIMATION_END);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 4dd147e53588..717c5774f9f3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -68,9 +68,7 @@ import java.util.Set;
* This class represents an active client session. There is generally one
* Session object per process that is interacting with the window manager.
*/
-// Needs to be public and not final so we can mock during tests...sucks I know :(
-public class Session extends IWindowSession.Stub
- implements IBinder.DeathRecipient {
+class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
final IInputMethodClient mClient;
@@ -83,6 +81,7 @@ public class Session extends IWindowSession.Stub
private final Set<WindowSurfaceController> mAppOverlaySurfaces = new HashSet<>();
// Set of visible alert window surfaces connected to this session.
private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>();
+ private final DragDropController mDragDropController;
final boolean mCanAddInternalSystemWindow;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
@@ -108,6 +107,7 @@ public class Session extends IWindowSession.Stub
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
== PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
+ mDragDropController = mService.mDragDropController;
StringBuilder sb = new StringBuilder();
sb.append("Session{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
@@ -306,190 +306,68 @@ public class Session extends IWindowSession.Stub
/* Drag/drop */
@Override
- public IBinder prepareDrag(IWindow window, int flags,
- int width, int height, Surface outSurface) {
- return mService.prepareDragSurface(window, mSurfaceSession, flags,
- width, height, outSurface);
+ public IBinder prepareDrag(IWindow window, int flags, int width, int height,
+ Surface outSurface) {
+ final int callerPid = Binder.getCallingPid();
+ final int callerUid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mDragDropController.prepareDrag(
+ mService, mSurfaceSession, callerPid, callerUid, window, flags, width, height,
+ outSurface);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
public boolean performDrag(IWindow window, IBinder dragToken,
int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
ClipData data) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
- }
-
- synchronized (mService.mWindowMap) {
- if (mService.mDragState == null) {
- Slog.w(TAG_WM, "No drag prepared");
- throw new IllegalStateException("performDrag() without prepareDrag()");
- }
-
- if (dragToken != mService.mDragState.mToken) {
- Slog.w(TAG_WM, "Performing mismatched drag");
- throw new IllegalStateException("performDrag() does not match prepareDrag()");
- }
-
- WindowState callingWin = mService.windowForClientLocked(null, window, false);
- if (callingWin == null) {
- Slog.w(TAG_WM, "Bad requesting window " + window);
- return false; // !!! TODO: throw here?
- }
-
- // !!! TODO: if input is not still focused on the initiating window, fail
- // the drag initiation (e.g. an alarm window popped up just as the application
- // called performDrag()
-
- mService.mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
-
- // !!! TODO: extract the current touch (x, y) in screen coordinates. That
- // will let us eliminate the (touchX,touchY) parameters from the API.
-
- // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
- // the actual drag event dispatch stuff in the dragstate
-
- final DisplayContent displayContent = callingWin.getDisplayContent();
- if (displayContent == null) {
- return false;
- }
- Display display = displayContent.getDisplay();
- mService.mDragState.register(display);
- if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
- mService.mDragState.getInputChannel())) {
- Slog.e(TAG_WM, "Unable to transfer touch focus");
- mService.mDragState.unregister();
- mService.mDragState.reset();
- mService.mDragState = null;
- return false;
- }
-
- mService.mDragState.mDisplayContent = displayContent;
- mService.mDragState.mData = data;
- mService.mDragState.broadcastDragStartedLw(touchX, touchY);
- mService.mDragState.overridePointerIconLw(touchSource);
-
- // remember the thumb offsets for later
- mService.mDragState.mThumbOffsetX = thumbCenterX;
- mService.mDragState.mThumbOffsetY = thumbCenterY;
-
- // Make the surface visible at the proper location
- final SurfaceControl surfaceControl = mService.mDragState.mSurfaceControl;
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
- TAG_WM, ">>> OPEN TRANSACTION performDrag");
- mService.openSurfaceTransaction();
- try {
- surfaceControl.setPosition(touchX - thumbCenterX,
- touchY - thumbCenterY);
- surfaceControl.setLayer(mService.mDragState.getDragLayerLw());
- surfaceControl.setLayerStack(display.getLayerStack());
- surfaceControl.show();
- } finally {
- mService.closeSurfaceTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
- TAG_WM, "<<< CLOSE TRANSACTION performDrag");
- }
-
- mService.mDragState.notifyLocationLw(touchX, touchY);
- }
-
- return true; // success!
+ return mDragDropController.performDrag(mService, window, dragToken, touchSource,
+ touchX, touchY, thumbCenterX, thumbCenterY, data);
}
@Override
- public boolean startMovingTask(IWindow window, float startX, float startY) {
- if (DEBUG_TASK_POSITIONING) Slog.d(
- TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
-
- long ident = Binder.clearCallingIdentity();
+ public void reportDropResult(IWindow window, boolean consumed) {
+ final long ident = Binder.clearCallingIdentity();
try {
- return mService.startMovingTask(window, startX, startY);
+ mDragDropController.reportDropResult(mService, window, consumed);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
- public void reportDropResult(IWindow window, boolean consumed) {
- IBinder token = window.asBinder();
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
- }
-
- synchronized (mService.mWindowMap) {
- long ident = Binder.clearCallingIdentity();
- try {
- if (mService.mDragState == null) {
- // Most likely the drop recipient ANRed and we ended the drag
- // out from under it. Log the issue and move on.
- Slog.w(TAG_WM, "Drop result given but no drag in progress");
- return;
- }
-
- if (mService.mDragState.mToken != token) {
- // We're in a drag, but the wrong window has responded.
- Slog.w(TAG_WM, "Invalid drop-result claim by " + window);
- throw new IllegalStateException("reportDropResult() by non-recipient");
- }
-
- // The right window has responded, even if it's no longer around,
- // so be sure to halt the timeout even if the later WindowState
- // lookup fails.
- mService.mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
- WindowState callingWin = mService.windowForClientLocked(null, window, false);
- if (callingWin == null) {
- Slog.w(TAG_WM, "Bad result-reporting window " + window);
- return; // !!! TODO: throw here?
- }
-
- mService.mDragState.mDragResult = consumed;
- mService.mDragState.endDragLw();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- @Override
public void cancelDragAndDrop(IBinder dragToken) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "cancelDragAndDrop");
- }
-
- synchronized (mService.mWindowMap) {
- long ident = Binder.clearCallingIdentity();
- try {
- if (mService.mDragState == null) {
- Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
- throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
- }
-
- if (mService.mDragState.mToken != dragToken) {
- Slog.w(TAG_WM,
- "cancelDragAndDrop() does not match prepareDrag()");
- throw new IllegalStateException(
- "cancelDragAndDrop() does not match prepareDrag()");
- }
-
- mService.mDragState.mDragResult = false;
- mService.mDragState.cancelDragLw();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mDragDropController.cancelDragAndDrop(mService, dragToken);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@Override
public void dragRecipientEntered(IWindow window) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
- }
+ mDragDropController.dragRecipientEntered(window);
}
@Override
public void dragRecipientExited(IWindow window) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
+ mDragDropController.dragRecipientExited(window);
+ }
+
+ @Override
+ public boolean startMovingTask(IWindow window, float startX, float startY) {
+ if (DEBUG_TASK_POSITIONING) Slog.d(
+ TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mService.startMovingTask(window, startX, startY);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 1fda832dd5be..aff1bc62563f 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -152,7 +152,8 @@ public class StackWindowController
}
}
- public void positionChildAtBottom(TaskWindowContainerController child) {
+ public void positionChildAtBottom(TaskWindowContainerController child,
+ boolean includingParents) {
if (child == null) {
// TODO: Fix the call-points that cause this to happen.
return;
@@ -164,7 +165,7 @@ public class StackWindowController
Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
return;
}
- mContainer.positionChildAt(POSITION_BOTTOM, childTask, false /* includingParents */);
+ mContainer.positionChildAt(POSITION_BOTTOM, childTask, includingParents);
if (mService.mAppTransition.isTransitionSet()) {
childTask.setSendingToBottom(true);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 5e5c6c5351af..791accf8f347 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1008,10 +1008,13 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
void resetAdjustedForIme(boolean adjustBoundsNow) {
if (adjustBoundsNow) {
mImeWin = null;
- mAdjustedForIme = false;
mImeGoingAway = false;
mAdjustImeAmount = 0f;
mAdjustDividerAmount = 0f;
+ if (!mAdjustedForIme) {
+ return;
+ }
+ mAdjustedForIme = false;
updateAdjustedBounds();
mService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
} else {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index c01ee31e8c1c..e409a68f2dfe 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -223,10 +223,6 @@ public class WindowAnimator {
}
}
- if (mService.mDragState != null) {
- mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
- }
-
if (!mAnimating) {
cancelAnimation();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 01f2ccb46513..ad6fc39d91f8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -357,8 +357,6 @@ public class WindowManagerService extends IWindowManager.Stub
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
- private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
-
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
@@ -752,6 +750,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mAllowTheaterModeWakeFromLayout;
TaskPositioner mTaskPositioner;
+ final DragDropController mDragDropController = new DragDropController();
DragState mDragState = null;
// For frozen screen animations.
@@ -4637,73 +4636,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
// -------------------------------------------------------------
- // Drag and drop
- // -------------------------------------------------------------
-
- IBinder prepareDragSurface(IWindow window, SurfaceSession session,
- int flags, int width, int height, Surface outSurface) {
- if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height
- + " flags=" + Integer.toHexString(flags) + " win=" + window
- + " asbinder=" + window.asBinder());
- }
-
- final int callerPid = Binder.getCallingPid();
- final int callerUid = Binder.getCallingUid();
- final long origId = Binder.clearCallingIdentity();
- IBinder token = null;
-
- try {
- synchronized (mWindowMap) {
- try {
- if (mDragState == null) {
- // TODO(multi-display): support other displays
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final Display display = displayContent.getDisplay();
-
- SurfaceControl surface = new SurfaceControl(session, "drag surface",
- width, height, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
- surface.setLayerStack(display.getLayerStack());
- float alpha = 1;
- if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
- alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
- }
- surface.setAlpha(alpha);
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG "
- + surface + ": CREATE");
- outSurface.copyFrom(surface);
- final IBinder winBinder = window.asBinder();
- token = new Binder();
- mDragState = new DragState(this, token, surface, flags, winBinder);
- mDragState.mPid = callerPid;
- mDragState.mUid = callerUid;
- mDragState.mOriginalAlpha = alpha;
- token = mDragState.mToken = new Binder();
-
- // 5 second timeout for this window to actually begin the drag
- mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
- Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
- mH.sendMessageDelayed(msg, 5000);
- } else {
- Slog.w(TAG_WM, "Drag already in progress");
- }
- } catch (OutOfResourcesException e) {
- Slog.e(TAG_WM, "Can't allocate drag surface w=" + width + " h=" + height, e);
- if (mDragState != null) {
- mDragState.reset();
- mDragState = null;
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
-
- return token;
- }
-
- // -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
@@ -4866,6 +4798,7 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int REPORT_WINDOWS_CHANGE = 19;
public static final int DRAG_START_TIMEOUT = 20;
public static final int DRAG_END_TIMEOUT = 21;
+
public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
public static final int BOOT_TIMEOUT = 23;
public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
@@ -5120,47 +5053,12 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case DRAG_START_TIMEOUT: {
- IBinder win = (IBinder)msg.obj;
- if (DEBUG_DRAG) {
- Slog.w(TAG_WM, "Timeout starting drag by win " + win);
- }
- synchronized (mWindowMap) {
- // !!! TODO: ANR the app that has failed to start the drag in time
- if (mDragState != null) {
- mDragState.unregister();
- mDragState.reset();
- mDragState = null;
- }
- }
- break;
- }
-
- case DRAG_END_TIMEOUT: {
- IBinder win = (IBinder)msg.obj;
- if (DEBUG_DRAG) {
- Slog.w(TAG_WM, "Timeout ending drag to win " + win);
- }
- synchronized (mWindowMap) {
- // !!! TODO: ANR the drag-receiving app
- if (mDragState != null) {
- mDragState.mDragResult = false;
- mDragState.endDragLw();
- }
- }
- break;
- }
-
+ case DRAG_START_TIMEOUT:
+ case DRAG_END_TIMEOUT:
case TEAR_DOWN_DRAG_AND_DROP_INPUT: {
- if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag ending; tearing down input channel");
- DragState.InputInterceptor interceptor = (DragState.InputInterceptor) msg.obj;
- if (interceptor != null) {
- synchronized (mWindowMap) {
- interceptor.tearDown();
- }
- }
+ mDragDropController.handleMessage(WindowManagerService.this, msg);
+ break;
}
- break;
case REPORT_HARD_KEYBOARD_STATUS_CHANGE: {
notifyHardKeyboardStatusChange();
@@ -7709,7 +7607,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean hasWideColorGamutSupport() {
- return mHasWideColorGamutSupport;
+ return mHasWideColorGamutSupport &&
+ !SystemProperties.getBoolean("persist.sys.sf.native_mode", false);
}
void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c59f44e7e707..80f6a4b9f258 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -175,6 +175,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -1682,6 +1683,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return getCallingUid() == Process.myUid();
}
+ void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+ Binder.withCleanCallingIdentity(action);
+ }
+
final int userHandleGetCallingUserId() {
return UserHandle.getUserId(binderGetCallingUid());
}
@@ -9023,6 +9028,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public boolean setTime(ComponentName who, long millis) {
+ Preconditions.checkNotNull(who, "ComponentName is null in setTime");
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ // Don't allow set time when auto time is on.
+ if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
+ return false;
+ }
+ mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis));
+ return true;
+ }
+
+ @Override
+ public boolean setTimeZone(ComponentName who, String timeZone) {
+ Preconditions.checkNotNull(who, "ComponentName is null in setTimeZone");
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ // Don't allow set timezone when auto timezone is on.
+ if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
+ return false;
+ }
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.getAlarmManager().setTimeZone(timeZone));
+ return true;
+ }
+
+ @Override
public void setSecureSetting(ComponentName who, String setting, String value) {
Preconditions.checkNotNull(who, "ComponentName is null");
int callingUserId = mInjector.userHandleGetCallingUserId();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d9db22e9b62d..b78fcddee119 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1551,6 +1551,11 @@ public final class SystemServer {
traceEnd();
}
+ // Statsd helper
+ traceBeginAndSlog("StartStatsCompanionService");
+ mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
+ traceEnd();
+
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
diff --git a/services/net/java/android/net/util/VersionedBroadcastListener.java b/services/net/java/android/net/util/VersionedBroadcastListener.java
new file mode 100644
index 000000000000..115aa464f4f1
--- /dev/null
+++ b/services/net/java/android/net/util/VersionedBroadcastListener.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.util.Log;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+
+/**
+ * A utility class that runs the provided callback on the provided handler when
+ * intents matching the provided filter arrive. Intents received by a stale
+ * receiver are safely ignored.
+ *
+ * Calls to startListening() and stopListening() must happen on the same thread.
+ *
+ * @hide
+ */
+public class VersionedBroadcastListener {
+ private static final boolean DBG = false;
+
+ public interface IntentCallback {
+ public void run(Intent intent);
+ }
+
+ private final String mTag;
+ private final Context mContext;
+ private final Handler mHandler;
+ private final IntentFilter mFilter;
+ private final Consumer<Intent> mCallback;
+ private final AtomicInteger mGenerationNumber;
+ private BroadcastReceiver mReceiver;
+
+ public VersionedBroadcastListener(String tag, Context ctx, Handler handler,
+ IntentFilter filter, Consumer<Intent> callback) {
+ mTag = tag;
+ mContext = ctx;
+ mHandler = handler;
+ mFilter = filter;
+ mCallback = callback;
+ mGenerationNumber = new AtomicInteger(0);
+ }
+
+ public int generationNumber() {
+ return mGenerationNumber.get();
+ }
+
+ public void startListening() {
+ if (DBG) Log.d(mTag, "startListening");
+ if (mReceiver != null) return;
+
+ mReceiver = new Receiver(mTag, mGenerationNumber, mCallback);
+ mContext.registerReceiver(mReceiver, mFilter, null, mHandler);
+ }
+
+ public void stopListening() {
+ if (DBG) Log.d(mTag, "stopListening");
+ if (mReceiver == null) return;
+
+ mGenerationNumber.incrementAndGet();
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+
+ private static class Receiver extends BroadcastReceiver {
+ public final String tag;
+ public final AtomicInteger atomicGenerationNumber;
+ public final Consumer<Intent> callback;
+ // Used to verify this receiver is still current.
+ public final int generationNumber;
+
+ public Receiver(
+ String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
+ this.tag = tag;
+ this.atomicGenerationNumber = atomicGenerationNumber;
+ this.callback = callback;
+ generationNumber = atomicGenerationNumber.incrementAndGet();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int currentGenerationNumber = atomicGenerationNumber.get();
+
+ if (DBG) {
+ Log.d(tag, "receiver generationNumber=" + generationNumber +
+ ", current generationNumber=" + currentGenerationNumber);
+ }
+ if (generationNumber != currentGenerationNumber) return;
+
+ callback.accept(intent);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 679be1d40b60..cb13e85157aa 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -51,13 +51,8 @@ import org.junit.Test;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private final ComponentName testActivityComponent =
- ComponentName.unflattenFromString("com.foo/.BarActivity");
- private final ComponentName secondaryActivityComponent =
- ComponentName.unflattenFromString("com.foo/.BarActivity2");
-
private ActivityManagerService mService;
- private ActivityStack mStack;
+ private TestActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@@ -69,20 +64,20 @@ public class ActivityRecordTests extends ActivityTestsBase {
mService = createActivityManagerService();
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
- mActivity = createActivity(mService, testActivityComponent, mTask);
+ mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+ mActivity = new ActivityBuilder(mService).setTask(mTask).build();
}
@Test
public void testStackCleanupOnClearingTask() throws Exception {
mActivity.setTask(null);
- assertEquals(getActivityRemovedFromStackCount(), 1);
+ assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
}
@Test
public void testStackCleanupOnActivityRemoval() throws Exception {
mTask.removeActivity(mActivity);
- assertEquals(getActivityRemovedFromStackCount(), 1);
+ assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
}
@Test
@@ -94,10 +89,10 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testNoCleanupMovingActivityInSameStack() throws Exception {
- final TaskRecord newTask =
- createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+ final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
+ .build();
mActivity.reparent(newTask, 0, null /*reason*/);
- assertEquals(getActivityRemovedFromStackCount(), 0);
+ assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
}
@Test
@@ -129,15 +124,6 @@ public class ActivityRecordTests extends ActivityTestsBase {
assertEquals(expectedActivityBounds, mActivity.getBounds());
}
- private int getActivityRemovedFromStackCount() {
- if (mStack instanceof ActivityStackReporter) {
- return ((ActivityStackReporter) mStack).onActivityRemovedFromStackInvocationCount();
- }
-
- return -1;
- }
-
-
@Test
public void testCanBeLaunchedOnDisplay() throws Exception {
testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
@@ -158,13 +144,13 @@ public class ActivityRecordTests extends ActivityTestsBase {
mService.mSupportsMultiWindow = true;
final TaskRecord task = taskPresent
- ? createTask(mService.mStackSupervisor, testActivityComponent, mStack) : null;
+ ? new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build() : null;
if (task != null) {
task.setResizeMode(taskResizeable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE);
}
- final ActivityRecord record = createActivity(mService, secondaryActivityComponent, task);
+ final ActivityRecord record = new ActivityBuilder(mService).setTask(task).build();
record.info.resizeMode = activityResizeable
? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 60a55fa941a7..fc17da843a9a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -51,9 +51,6 @@ import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
- private final ComponentName testActivityComponent =
- ComponentName.unflattenFromString("com.foo/.BarActivity");
-
private ActivityManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityStack mFullscreenStack;
@@ -87,15 +84,14 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
*/
@Test
public void testReplacingTaskInPinnedStack() throws Exception {
- final TaskRecord firstTask = createTask(
- mSupervisor, testActivityComponent, mFullscreenStack);
- final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
- firstTask);
- // Create a new task on the full screen stack
- final TaskRecord secondTask = createTask(
- mSupervisor, testActivityComponent, mFullscreenStack);
- final ActivityRecord secondActivity = createActivity(mService, testActivityComponent,
- secondTask);
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final TaskRecord firstTask = firstActivity.getTask();
+
+ final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final TaskRecord secondTask = secondActivity.getTask();
+
mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
// Ensure full screen stack has both tasks.
@@ -104,7 +100,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
// Move first activity to pinned stack.
final Rect sourceBounds = new Rect();
mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
- 0f /*aspectRatio*/, false, "initialMove");
+ 0f /*aspectRatio*/, "initialMove");
final ActivityDisplay display = mFullscreenStack.getDisplay();
ActivityStack pinnedStack = display.getPinnedStack();
@@ -114,7 +110,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
// Move second activity to pinned stack.
mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
- 0f /*aspectRatio*/, false, "secondMove");
+ 0f /*aspectRatio*/, "secondMove");
// Need to get stacks again as a new instance might have been created.
pinnedStack = display.getPinnedStack();
@@ -142,10 +138,8 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
*/
@Test
public void testStoppingActivityRemovedWhenResumed() throws Exception {
- final TaskRecord firstTask = createTask(
- mSupervisor, testActivityComponent, mFullscreenStack);
- final ActivityRecord firstActivity = createActivity(mService, testActivityComponent,
- firstTask);
+ final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
mSupervisor.mStoppingActivities.add(firstActivity);
firstActivity.completeResumeLocked();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 4ee1f47d02e0..480b210c17b8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -16,13 +16,21 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
@@ -45,12 +53,6 @@ import org.junit.Test;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
- private static final int TEST_STACK_ID = 100;
- private static final ComponentName testActivityComponent =
- ComponentName.unflattenFromString("com.foo/.BarActivity");
- private static final ComponentName testOverlayComponent =
- ComponentName.unflattenFromString("com.foo/.OverlayActivity");
-
private ActivityManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityStack mStack;
@@ -65,7 +67,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mSupervisor = mService.mStackSupervisor;
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask = createTask(mSupervisor, testActivityComponent, mStack);
+ mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
}
@Test
@@ -77,7 +79,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testOccupiedTaskCleanupOnRemove() throws Exception {
- final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
assertNotNull(mTask.getWindowContainerController());
mStack.removeTask(mTask, "testOccupiedTaskCleanupOnRemove", REMOVE_TASK_MODE_DESTROYING);
assertNotNull(mTask.getWindowContainerController());
@@ -85,7 +87,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testNoPauseDuringResumeTopActivity() throws Exception {
- final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
// Simulate the a resumed activity set during
// {@link ActivityStack#resumeTopActivityUncheckedLocked}.
@@ -103,7 +105,7 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testStopActivityWhenActivityDestroyed() throws Exception {
- final ActivityRecord r = createActivity(mService, testActivityComponent, mTask);
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
mSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", mStack);
mStack.stopActivityLocked(r);
@@ -113,18 +115,140 @@ public class ActivityStackTests extends ActivityTestsBase {
@Test
public void testFindTaskWithOverlay() throws Exception {
- final ActivityRecord r = createActivity(mService, testActivityComponent, mTask, 0);
+ final ActivityRecord r = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(mStack)
+ .setUid(0)
+ .build();
+ final TaskRecord task = r.getTask();
// Overlay must be for a different user to prevent recognizing a matching top activity
- final ActivityRecord taskOverlay = createActivity(mService, testOverlayComponent, mTask,
- UserHandle.PER_USER_RANGE * 2);
+ final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
+ .setUid(UserHandle.PER_USER_RANGE * 2).build();
taskOverlay.mTaskOverlay = true;
final ActivityStackSupervisor.FindTaskResult result =
new ActivityStackSupervisor.FindTaskResult();
mStack.findTaskLocked(r, result);
- assertEquals(mTask.getTopActivity(false /* includeOverlays */), r);
- assertEquals(mTask.getTopActivity(true /* includeOverlays */), taskOverlay);
+ assertEquals(task.getTopActivity(false /* includeOverlays */), r);
+ assertEquals(task.getTopActivity(true /* includeOverlays */), taskOverlay);
assertNotNull(result.r);
}
+
+ @Test
+ public void testShouldBeVisible_Fullscreen() throws Exception {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+
+ final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
+ // should be visible since it is always on-top.
+ fullscreenStack.setIsTranslucent(false);
+ assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+ assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
+
+ // Home stack should be visible behind a translucent fullscreen stack.
+ fullscreenStack.setIsTranslucent(true);
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+ }
+
+ @Test
+ public void testShouldBeVisible_SplitScreen() throws Exception {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+ // Home stack shouldn't be visible if both halves of split-screen are opaque.
+ splitScreenPrimary.setIsTranslucent(false);
+ splitScreenSecondary.setIsTranslucent(false);
+ assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+ // Home stack should be visible if one of the halves of split-screen is translucent.
+ splitScreenPrimary.setIsTranslucent(true);
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+
+ final TestActivityStack splitScreenSecondary2 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ // First split-screen secondary shouldn't be visible behind another opaque split-split
+ // secondary.
+ splitScreenSecondary2.setIsTranslucent(false);
+ assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+ // First split-screen secondary should be visible behind another translucent split-split
+ // secondary.
+ splitScreenSecondary2.setIsTranslucent(true);
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+ final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+
+ // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
+ assistantStack.setIsTranslucent(false);
+ assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+
+ // Split-screen stacks should be visible behind a translucent fullscreen stack.
+ assistantStack.setIsTranslucent(true);
+ assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
+ assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
+ }
+
+ @Test
+ public void testShouldBeVisible_Finishing() throws Exception {
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ translucentStack.setIsTranslucent(true);
+
+ assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+
+ final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+ topRunningHomeActivity.finishing = true;
+ final ActivityRecord topRunningTranslucentActivity =
+ translucentStack.topRunningActivityLocked();
+ topRunningTranslucentActivity.finishing = true;
+
+ // Home shouldn't be visible since its activity is marked as finishing and it isn't the top
+ // of the stack list.
+ assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ // Home should be visible if we are starting an activity within it.
+ assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
+ // The translucent stack should be visible since it is the top of the stack list even though
+ // it has its activity marked as finishing.
+ assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+ }
+
+ private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
+ ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
+ final T stack = display.createStack(windowingMode, activityType, onTop);
+ final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
+ .setCreateTask(true).build();
+ return stack;
+ }
}
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 026abce42b7d..5b1e4b731d38 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -50,9 +50,6 @@ import static org.mockito.Mockito.times;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStarterTests extends ActivityTestsBase {
- private static final ComponentName testActivityComponent =
- ComponentName.unflattenFromString("com.foo/.BarActivity");
-
private ActivityManagerService mService;
private ActivityStarter mStarter;
@@ -66,9 +63,10 @@ public class ActivityStarterTests extends ActivityTestsBase {
@Test
public void testUpdateLaunchBounds() throws Exception {
// When in a non-resizeable stack, the task bounds should be updated.
- final TaskRecord task = createTask(mService.mStackSupervisor, testActivityComponent,
- mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+ final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+ .build();
final Rect bounds = new Rect(10, 10, 100, 100);
mStarter.updateBounds(task, bounds);
@@ -76,9 +74,10 @@ public class ActivityStarterTests extends ActivityTestsBase {
assertEquals(task.getStack().mBounds, null);
// When in a resizeable stack, the stack bounds should be updated as well.
- final TaskRecord task2 = createTask(mService.mStackSupervisor, testActivityComponent,
- mService.mStackSupervisor.getDefaultDisplay().createStack(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+ final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+ .build();
assertTrue(task2.getStack() instanceof PinnedActivityStack);
mStarter.updateBounds(task2, bounds);
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 20077f3e94b0..3d5d87c1359a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.mockito.Mockito.mock;
@@ -52,12 +54,20 @@ import org.mockito.MockitoAnnotations;
* A base class to handle common operations in activity related unit tests.
*/
public class ActivityTestsBase {
+ private static boolean sOneTimeSetupDone = false;
+
private final Context mContext = InstrumentationRegistry.getContext();
private HandlerThread mHandlerThread;
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
+ if (!sOneTimeSetupDone) {
+ sOneTimeSetupDone = true;
+
+ // Allows to mock package local classes and methods
+ System.setProperty("dexmaker.share_classloader", "true");
+ MockitoAnnotations.initMocks(this);
+ }
mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
mHandlerThread.start();
}
@@ -73,57 +83,156 @@ public class ActivityTestsBase {
return service;
}
- protected static ActivityRecord createActivity(ActivityManagerService service,
- ComponentName component, TaskRecord task) {
- return createActivity(service, component, task, 0 /* userId */);
- }
+ /**
+ * Builder for creating new activities.
+ */
+ protected static class ActivityBuilder {
+ // An id appended to the end of the component name to make it unique
+ private static int sCurrentActivityId = 0;
- protected static ActivityRecord createActivity(ActivityManagerService service,
- ComponentName component, TaskRecord task, int uid) {
- Intent intent = new Intent();
- intent.setComponent(component);
- final ActivityInfo aInfo = new ActivityInfo();
- aInfo.applicationInfo = new ApplicationInfo();
- aInfo.applicationInfo.packageName = component.getPackageName();
- aInfo.applicationInfo.uid = uid;
- AttributeCache.init(service.mContext);
- final ActivityRecord activity = new ActivityRecord(service, null /* caller */,
- 0 /* launchedFromPid */, 0, null, intent, null,
- aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
- 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
- service.mStackSupervisor, null /* options */, null /* sourceRecord */);
- activity.mWindowContainerController = mock(AppWindowContainerController.class);
-
- if (task != null) {
- task.addActivityToTop(activity);
- }
-
- return activity;
- }
+ // Default package name
+ private static final String DEFAULT_PACKAGE = "com.foo";
+
+ // Default base activity name
+ private static final String DEFAULT_BASE_ACTIVITY_NAME = ".BarActivity";
+
+ private final ActivityManagerService mService;
+
+ private ComponentName mComponent;
+ private TaskRecord mTaskRecord;
+ private int mUid;
+ private boolean mCreateTask;
+ private ActivityStack mStack;
+
+ ActivityBuilder(ActivityManagerService service) {
+ mService = service;
+ }
+
+ ActivityBuilder setComponent(ComponentName component) {
+ mComponent = component;
+ return this;
+ }
+
+ ActivityBuilder setTask(TaskRecord task) {
+ mTaskRecord = task;
+ return this;
+ }
- protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
- ComponentName component, ActivityStack stack) {
- return createTask(supervisor, component, 0 /* flags */, 0 /* taskId */, stack);
+ ActivityBuilder setStack(ActivityStack stack) {
+ mStack = stack;
+ return this;
+ }
+
+ ActivityBuilder setCreateTask(boolean createTask) {
+ mCreateTask = createTask;
+ return this;
+ }
+
+ ActivityBuilder setUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+
+ ActivityRecord build() {
+ if (mComponent == null) {
+ final int id = sCurrentActivityId++;
+ mComponent = ComponentName.createRelative(DEFAULT_PACKAGE,
+ DEFAULT_BASE_ACTIVITY_NAME + id);
+ }
+
+ if (mCreateTask) {
+ mTaskRecord = new TaskBuilder(mService.mStackSupervisor)
+ .setComponent(mComponent)
+ .setStack(mStack).build();
+ }
+
+ Intent intent = new Intent();
+ intent.setComponent(mComponent);
+ final ActivityInfo aInfo = new ActivityInfo();
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.packageName = mComponent.getPackageName();
+ aInfo.applicationInfo.uid = mUid;
+ AttributeCache.init(mService.mContext);
+ final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
+ 0 /* launchedFromPid */, 0, null, intent, null,
+ aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
+ 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
+ mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
+ activity.mWindowContainerController = mock(AppWindowContainerController.class);
+
+ if (mTaskRecord != null) {
+ mTaskRecord.addActivityToTop(activity);
+ }
+
+ return activity;
+ }
}
- protected static TaskRecord createTask(ActivityStackSupervisor supervisor,
- ComponentName component, int flags, int taskId, ActivityStack stack) {
- final ActivityInfo aInfo = new ActivityInfo();
- aInfo.applicationInfo = new ApplicationInfo();
- aInfo.applicationInfo.packageName = component.getPackageName();
+ /**
+ * Builder for creating new tasks.
+ */
+ protected static class TaskBuilder {
+ private final ActivityStackSupervisor mSupervisor;
+
+ private ComponentName mComponent;
+ private String mPackage;
+ private int mFlags = 0;
+ private int mTaskId = 0;
+
+ private ActivityStack mStack;
+
+ TaskBuilder(ActivityStackSupervisor supervisor) {
+ mSupervisor = supervisor;
+ }
+
+ TaskBuilder setComponent(ComponentName component) {
+ mComponent = component;
+ return this;
+ }
+
+ TaskBuilder setPackage(String packageName) {
+ mPackage = packageName;
+ return this;
+ }
+
+ TaskBuilder setFlags(int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ TaskBuilder setTaskId(int taskId) {
+ mTaskId = taskId;
+ return this;
+ }
+
+ TaskBuilder setStack(ActivityStack stack) {
+ mStack = stack;
+ return this;
+ }
+
+ TaskRecord build() {
+ if (mStack == null) {
+ mStack = mSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ }
+
+ final ActivityInfo aInfo = new ActivityInfo();
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.packageName = mPackage;
- Intent intent = new Intent();
- intent.setComponent(component);
- intent.setFlags(flags);
+ Intent intent = new Intent();
+ intent.setComponent(mComponent);
+ intent.setFlags(mFlags);
- final TaskRecord task = new TaskRecord(supervisor.mService, taskId, aInfo,
- intent /*intent*/, null /*_taskDescription*/);
- supervisor.setFocusStackUnchecked("test", stack);
- stack.addTask(task, true, "creating test task");
- task.setStack(stack);
- task.setWindowContainerController(mock(TaskWindowContainerController.class));
+ final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
+ intent /*intent*/, null /*_taskDescription*/);
+ mSupervisor.setFocusStackUnchecked("test", mStack);
+ mStack.addTask(task, true, "creating test task");
+ task.setStack(mStack);
+ task.setWindowContainerController(mock(TaskWindowContainerController.class));
- return task;
+ return task;
+ }
}
/**
@@ -268,20 +377,21 @@ public class ActivityTestsBase {
return service;
}
- protected interface ActivityStackReporter {
- int onActivityRemovedFromStackInvocationCount();
- }
-
/**
* Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
* method is called. Note that its functionality depends on the implementations of the
* construction arguments.
*/
protected static class TestActivityStack<T extends StackWindowController>
- extends ActivityStack<T> implements ActivityStackReporter {
+ extends ActivityStack<T> {
private int mOnActivityRemovedFromStackCount = 0;
private T mContainerController;
+ static final int IS_TRANSLUCENT_UNSET = 0;
+ static final int IS_TRANSLUCENT_FALSE = 1;
+ static final int IS_TRANSLUCENT_TRUE = 2;
+ private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
+
TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
int windowingMode, int activityType, boolean onTop) {
super(display, stackId, supervisor, windowingMode, activityType, onTop);
@@ -294,8 +404,7 @@ public class ActivityTestsBase {
}
// Returns the number of times {@link #onActivityRemovedFromStack} has been called
- @Override
- public int onActivityRemovedFromStackInvocationCount() {
+ int onActivityRemovedFromStackInvocationCount() {
return mOnActivityRemovedFromStackCount;
}
@@ -309,5 +418,22 @@ public class ActivityTestsBase {
T getWindowContainerController() {
return mContainerController;
}
+
+ void setIsTranslucent(boolean isTranslucent) {
+ mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
+ }
+
+ @Override
+ boolean isStackTranslucent(ActivityRecord starting) {
+ switch (mIsTranslucent) {
+ case IS_TRANSLUCENT_TRUE:
+ return true;
+ case IS_TRANSLUCENT_FALSE:
+ return false;
+ case IS_TRANSLUCENT_UNSET:
+ default:
+ return super.isStackTranslucent(starting);
+ }
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java
new file mode 100644
index 000000000000..de831a09c2ae
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchingActivityPositionerTests.java
@@ -0,0 +1,143 @@
+/*
+ * 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.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.doAnswer;
+
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
+
+/**
+ * Tests for exercising resizing bounds due to activity options.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.LaunchingActivityPositionerTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LaunchingActivityPositionerTests extends ActivityTestsBase {
+ private LaunchingActivityPositioner mPositioner;
+ private ActivityManagerService mService;
+ private ActivityStack mStack;
+ private TaskRecord mTask;
+ private ActivityRecord mActivity;
+
+ private Rect mCurrent;
+ private Rect mResult;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mService = createActivityManagerService();
+ mPositioner = new LaunchingActivityPositioner(mService.mStackSupervisor);
+ mCurrent = new Rect();
+ mResult = new Rect();
+
+
+ mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+ mActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ }
+
+
+ @Test
+ public void testSkippedInvocations() throws Exception {
+ // No specified activity should be ignored
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ null /*activity*/, null /*options*/, mCurrent, mResult));
+
+ // No specified activity options should be ignored
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, null /*options*/, mCurrent, mResult));
+
+ // launch bounds specified should be ignored.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+
+ // Non-resizeable records should be ignored
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ assertFalse(mActivity.isResizeable());
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+
+ // make record resizeable
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+ assertTrue(mActivity.isResizeable());
+
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+
+ // Does not support freeform
+ mService.mSupportsFreeformWindowManagement = false;
+ assertFalse(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options));
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+
+ mService.mSupportsFreeformWindowManagement = true;
+ options.setLaunchBounds(new Rect());
+ assertTrue(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options));
+
+ // Invalid bounds
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+ options.setLaunchBounds(new Rect(0, 0, -1, -1));
+ assertEquals(RESULT_SKIP, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+
+ // Valid bounds should cause the positioner to be applied.
+ options.setLaunchBounds(new Rect(0, 0, 100, 100));
+ assertEquals(RESULT_DONE, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+ }
+
+ @Test
+ public void testBoundsExtraction() throws Exception {
+ // Make activity resizeable and enable freeform mode.
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+ mService.mSupportsFreeformWindowManagement = true;
+
+ ActivityOptions options = ActivityOptions.makeBasic();
+ final Rect proposedBounds = new Rect(20, 30, 45, 40);
+ options.setLaunchBounds(proposedBounds);
+
+ assertEquals(RESULT_DONE, mPositioner.onCalculateBounds(null /*task*/, null /*layout*/,
+ mActivity, options /*options*/, mCurrent, mResult));
+ assertEquals(mResult, proposedBounds);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingBoundsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingBoundsControllerTests.java
new file mode 100644
index 000000000000..f24a273d66d1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchingBoundsControllerTests.java
@@ -0,0 +1,173 @@
+/*
+ * 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.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_DONE;
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_SKIP;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for exercising {@link LaunchingBoundsController}.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.LaunchingBoundsControllerTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LaunchingBoundsControllerTests extends ActivityTestsBase {
+ private LaunchingBoundsController mController;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mController = new LaunchingBoundsController();
+ }
+
+ /**
+ * Ensures positioners further down the chain are not called when RESULT_DONE is returned.
+ */
+ @Test
+ public void testEarlyExit() {
+ final LaunchingBoundsPositioner ignoredPositioner = mock(LaunchingBoundsPositioner.class);
+ final LaunchingBoundsPositioner earlyExitPositioner =
+ (task, layout, activity, options, current, result) -> RESULT_DONE;
+
+ mController.registerPositioner(ignoredPositioner);
+ mController.registerPositioner(earlyExitPositioner);
+
+ mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*options*/, new Rect());
+ verify(ignoredPositioner, never()).onCalculateBounds(any(), any(), any(), any(), any(),
+ any());
+ }
+
+ /**
+ * Ensures that positioners are called in the correct order.
+ */
+ @Test
+ public void testRegistration() {
+ LaunchingBoundsPositioner earlyExitPositioner =
+ new InstrumentedPositioner(RESULT_DONE, new Rect());
+
+ final LaunchingBoundsPositioner firstPositioner = spy(earlyExitPositioner);
+
+ mController.registerPositioner(firstPositioner);
+
+ mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*options*/, new Rect());
+ verify(firstPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
+ any());
+
+ final LaunchingBoundsPositioner secondPositioner = spy(earlyExitPositioner);
+
+ mController.registerPositioner(secondPositioner);
+
+ mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*options*/, new Rect());
+ verify(firstPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
+ any());
+ verify(secondPositioner, times(1)).onCalculateBounds(any(), any(), any(), any(), any(),
+ any());
+ }
+
+ /**
+ * Makes sure positioners further down the registration chain are called.
+ */
+ @Test
+ public void testPassThrough() {
+ final LaunchingBoundsPositioner positioner1 = mock(LaunchingBoundsPositioner.class);
+ final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
+ new Rect (0, 0, 30, 20));
+
+ mController.registerPositioner(positioner1);
+ mController.registerPositioner(positioner2);
+
+ mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*options*/, new Rect());
+
+ verify(positioner1, times(1)).onCalculateBounds(any(), any(), any(), any(),
+ eq(positioner2.getLaunchBounds()), any());
+ }
+
+ /**
+ * Ensures skipped results are not propagated.
+ */
+ @Test
+ public void testSkip() {
+ final InstrumentedPositioner positioner1 =
+ new InstrumentedPositioner(RESULT_SKIP, new Rect(0, 0, 10, 10));
+
+
+ final InstrumentedPositioner positioner2 =
+ new InstrumentedPositioner(RESULT_CONTINUE, new Rect(0, 0, 20, 30));
+
+ mController.registerPositioner(positioner1);
+ mController.registerPositioner(positioner2);
+
+ final Rect resultBounds = new Rect();
+
+ mController.calculateBounds(null /*task*/, null /*layout*/, null /*activity*/,
+ null /*options*/, resultBounds);
+
+ assertEquals(resultBounds, positioner2.getLaunchBounds());
+ }
+
+ public static class InstrumentedPositioner implements LaunchingBoundsPositioner {
+ private int mReturnVal;
+ private Rect mBounds;
+ InstrumentedPositioner(int returnVal, Rect bounds) {
+ mReturnVal = returnVal;
+ mBounds = bounds;
+ }
+
+ @Override
+ public int onCalculateBounds(TaskRecord task, ActivityInfo.WindowLayout layout,
+ ActivityRecord activity, ActivityOptions options, Rect current, Rect result) {
+ result.set(mBounds);
+ return mReturnVal;
+ }
+
+ Rect getLaunchBounds() {
+ return mBounds;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
index e6d683120e79..30666c0455f8 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java
@@ -17,15 +17,12 @@
package com.android.server.am;
import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.WindowLayout;
-import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
import android.view.Gravity;
import org.junit.runner.RunWith;
import org.junit.Before;
@@ -33,10 +30,11 @@ import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
-import java.util.ArrayList;
-
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
+import static com.android.server.am.LaunchingBoundsController.LaunchingBoundsPositioner.RESULT_CONTINUE;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -48,17 +46,12 @@ import static org.junit.Assert.assertEquals;
* Tests for exercising resizing bounds.
*
* Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.am.LaunchBoundsTests
+ * bit FrameworksServicesTests:com.android.server.am.LaunchingTaskPositionerTests
*/
@MediumTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class LaunchBoundsTests extends ActivityTestsBase {
- private final ComponentName testActivityComponent =
- ComponentName.unflattenFromString("com.foo/.BarActivity");
- private final ComponentName testActivityComponent2 =
- ComponentName.unflattenFromString("com.foo/.BarActivity2");
-
+public class LaunchingTaskPositionerTests extends ActivityTestsBase {
private final static int STACK_WIDTH = 100;
private final static int STACK_HEIGHT = 200;
@@ -68,6 +61,11 @@ public class LaunchBoundsTests extends ActivityTestsBase {
private ActivityStack mStack;
private TaskRecord mTask;
+ private LaunchingTaskPositioner mPositioner;
+
+ private Rect mCurrent;
+ private Rect mResult;
+
@Before
@Override
public void setUp() throws Exception {
@@ -80,7 +78,12 @@ public class LaunchBoundsTests extends ActivityTestsBase {
// We must create the task after resizing to make sure it does not inherit the stack
// dimensions on resize.
- mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+ mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
+
+ mPositioner = new LaunchingTaskPositioner();
+
+ mResult = new Rect();
+ mCurrent = new Rect();
}
/**
@@ -101,12 +104,9 @@ public class LaunchBoundsTests extends ActivityTestsBase {
*/
@Test
public void testLaunchNoWindowLayout() throws Exception {
- final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
-
- mStack.layoutTaskInStack(mTask, null);
-
- // We expect the task to be placed in the middle of the screen with margins applied.
- assertEquals(mTask.mBounds, expectedTaskBounds);
+ assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask, null /*layout*/,
+ null /*record*/, null /*options*/, mCurrent, mResult));
+ assertEquals(getDefaultBounds(Gravity.NO_GRAVITY), mResult);
}
/**
@@ -116,11 +116,10 @@ public class LaunchBoundsTests extends ActivityTestsBase {
*/
@Test
public void testlaunchEmptyWindowLayout() throws Exception {
- final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
-
- WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
- mStack.layoutTaskInStack(mTask, layout);
- assertEquals(mTask.mBounds, expectedTaskBounds);
+ assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask,
+ new WindowLayout(0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0), null /*activity*/,
+ null /*options*/, mCurrent, mResult));
+ assertEquals(mResult, getDefaultBounds(Gravity.NO_GRAVITY));
}
/**
@@ -149,9 +148,15 @@ public class LaunchBoundsTests extends ActivityTestsBase {
}
private void testGravity(int gravity) {
- final WindowLayout gravityLayout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
- mStack.layoutTaskInStack(mTask, gravityLayout);
- assertEquals(mTask.mBounds, getDefaultBounds(gravity));
+ try {
+ assertEquals(RESULT_CONTINUE, mPositioner.onCalculateBounds(mTask,
+ new WindowLayout(0, 0, 0, 0, gravity, 0, 0), null /*activity*/,
+ null /*options*/, mCurrent, mResult));
+ assertEquals(mResult, getDefaultBounds(gravity));
+ } finally {
+ mCurrent.setEmpty();
+ mResult.setEmpty();
+ }
}
/**
@@ -174,7 +179,7 @@ public class LaunchBoundsTests extends ActivityTestsBase {
final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
// layout first task
- mStack.layoutTaskInStack(mTask, layout /*windowLayout*/);
+ mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(mTask, layout);
// Second task will be laid out on top of the first so starting bounds is the same.
final Rect expectedBounds = new Rect(mTask.mBounds);
@@ -185,14 +190,15 @@ public class LaunchBoundsTests extends ActivityTestsBase {
// wrap with try/finally to ensure cleanup of activity/stack.
try {
// empty tasks are ignored in conflicts
- activity = createActivity(mService, testActivityComponent, mTask);
+ activity = new ActivityBuilder(mService).setTask(mTask).build();
// Create secondary task
- secondTask = createTask(mService.mStackSupervisor, testActivityComponent,
- mStack);
+ secondTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
// layout second task
- mStack.layoutTaskInStack(secondTask, layout /*windowLayout*/);
+ assertEquals(RESULT_CONTINUE,
+ mPositioner.onCalculateBounds(secondTask, layout, null /*activity*/,
+ null /*options*/, mCurrent, mResult));
if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)
|| (gravity & (Gravity.BOTTOM | Gravity.RIGHT))
@@ -207,7 +213,7 @@ public class LaunchBoundsTests extends ActivityTestsBase {
LaunchingTaskPositioner.getVerticalStep(mStack.mBounds));
}
- assertEquals(secondTask.mBounds, expectedBounds);
+ assertEquals(mResult, expectedBounds);
} finally {
// Remove task and activity to prevent influencing future tests
if (activity != null) {
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 e607228efa24..939e989c5fe8 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -335,8 +335,9 @@ public class RecentTasksTest extends ActivityTestsBase {
}
private TaskRecord createTask(String className, int flags, int userId) {
- TaskRecord task = createTask(mService.mStackSupervisor, createComponent(className), flags,
- LAST_TASK_ID++, mStack);
+ 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;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 54717157d069..ae4b569ec56c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -15,6 +15,7 @@
*/
package com.android.server.devicepolicy;
+import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,11 +35,13 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.security.KeyChain;
+import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.IWindowManager;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.widget.LockPatternUtils;
import java.io.File;
@@ -194,6 +197,9 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
}
@Override
+ AlarmManager getAlarmManager() {return services.alarmManager;}
+
+ @Override
LockPatternUtils newLockPatternUtils() {
return services.lockPatternUtils;
}
@@ -234,6 +240,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
}
@Override
+ void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+ context.binder.withCleanCallingIdentity(action);
+ }
+
+ @Override
int binderGetCallingUid() {
return context.binder.getCallingUid();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a8bf8f18b09a..e1e9cf5bc013 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3090,6 +3090,47 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
}
+ public void testSetTime() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ dpm.setTime(admin1, 0);
+ verify(getServices().alarmManager).setTime(0);
+ }
+
+ public void testSetTimeFailWithPO() throws Exception {
+ setupProfileOwner();
+ assertExpectException(SecurityException.class, null, () -> dpm.setTime(admin1, 0));
+ }
+
+ public void testSetTimeWithAutoTimeOn() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME, 0))
+ .thenReturn(1);
+ assertFalse(dpm.setTime(admin1, 0));
+ }
+
+ public void testSetTimeZone() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ dpm.setTimeZone(admin1, "Asia/Shanghai");
+ verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+ }
+
+ public void testSetTimeZoneFailWithPO() throws Exception {
+ setupProfileOwner();
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setTimeZone(admin1, "Asia/Shanghai"));
+ }
+
+ public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME_ZONE, 0))
+ .thenReturn(1);
+ assertFalse(dpm.setTimeZone(admin1, "Asia/Shanghai"));
+ }
+
public void testGetLastBugReportRequestTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 97021181c384..7e11e87b9f22 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -30,8 +30,12 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.support.annotation.NonNull;
import android.test.mock.MockContext;
import android.util.ArrayMap;
+import android.util.ExceptionUtils;
+
+import com.android.internal.util.FunctionalUtils;
import org.junit.Assert;
@@ -95,6 +99,21 @@ public class DpmMockContext extends MockContext {
callingPid = (int) token;
}
+ public void withCleanCallingIdentity(@NonNull FunctionalUtils.ThrowingRunnable action) {
+ long callingIdentity = clearCallingIdentity();
+ Throwable throwableToPropagate = null;
+ try {
+ action.run();
+ } catch (Throwable throwable) {
+ throwableToPropagate = throwable;
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ if (throwableToPropagate != null) {
+ throw ExceptionUtils.propagate(throwableToPropagate);
+ }
+ }
+ }
+
public int getCallingUid() {
return callingUid;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java
index 7a676e258aba..bb35beb20a23 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java
@@ -154,9 +154,6 @@ public class UserDataPreparerTest {
File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID);
systemDeDir.mkdirs();
writeFile(new File(systemDeDir, "file"), "-----" );
- File miscDeDir = mUserDataPreparer.getDataMiscDeDirectory(TEST_USER_ID);
- miscDeDir.mkdirs();
- writeFile(new File(miscDeDir, "file"), "-----" );
mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_DE);
@@ -168,8 +165,6 @@ public class UserDataPreparerTest {
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(systemDir)));
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
systemDeDir)));
- assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
- miscDeDir)));
}
@Test
@@ -177,9 +172,6 @@ public class UserDataPreparerTest {
File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
systemCeDir.mkdirs();
writeFile(new File(systemCeDir, "file"), "-----" );
- File miscCeDir = mUserDataPreparer.getDataMiscCeDirectory(TEST_USER_ID);
- miscCeDir.mkdirs();
- writeFile(new File(miscCeDir, "file"), "-----" );
mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_CE);
@@ -190,8 +182,6 @@ public class UserDataPreparerTest {
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
systemCeDir)));
- assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
- miscCeDir)));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 42ddedf0b340..67ffe5847cbc 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,6 +16,11 @@
package com.android.server.usage;
+import static android.app.usage.AppStandby.REASON_TIMEOUT;
+import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
+
+import android.app.usage.AppStandby;
import android.os.FileUtils;
import android.test.AndroidTestCase;
@@ -28,6 +33,8 @@ public class AppIdleHistoryTests extends AndroidTestCase {
final static String PACKAGE_1 = "com.android.testpackage1";
final static String PACKAGE_2 = "com.android.testpackage2";
+ final static int USER_ID = 0;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -42,7 +49,6 @@ public class AppIdleHistoryTests extends AndroidTestCase {
}
public void testFilesCreation() {
- final int userId = 0;
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0);
aih.updateDisplay(true, /* elapsedRealtime= */ 1000);
@@ -50,9 +56,9 @@ public class AppIdleHistoryTests extends AndroidTestCase {
// Screen On time file should be written right away
assertTrue(aih.getScreenOnTimeFile().exists());
- aih.writeAppIdleTimes(userId);
+ aih.writeAppIdleTimes(USER_ID);
// stats file should be written now
- assertTrue(new File(new File(mStorageDir, "users/" + userId),
+ assertTrue(new File(new File(mStorageDir, "users/" + USER_ID),
AppIdleHistory.APP_IDLE_FILENAME).exists());
}
@@ -77,24 +83,33 @@ public class AppIdleHistoryTests extends AndroidTestCase {
assertEquals(aih2.getScreenOnTime(13000), 4000);
}
- public void testPackageEvents() {
+ public void testBuckets() {
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
- aih.setThresholds(4000, 1000);
- aih.updateDisplay(true, 1000);
- // App is not-idle by default
- assertFalse(aih.isIdle(PACKAGE_1, 0, 1500));
- // Still not idle
- assertFalse(aih.isIdle(PACKAGE_1, 0, 3000));
- // Idle now
- assertTrue(aih.isIdle(PACKAGE_1, 0, 8000));
- // Not idle
- assertFalse(aih.isIdle(PACKAGE_2, 0, 9000));
-
- // Screen off
- aih.updateDisplay(false, 9100);
- // Still idle after 10 seconds because screen hasn't been on long enough
- assertFalse(aih.isIdle(PACKAGE_2, 0, 20000));
- aih.updateDisplay(true, 21000);
- assertTrue(aih.isIdle(PACKAGE_2, 0, 23000));
+
+ aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
+ AppStandby.REASON_USAGE);
+ // ACTIVE means not idle
+ assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
+
+ aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
+ AppStandby.REASON_USAGE);
+ aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
+ REASON_TIMEOUT);
+
+ assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
+ assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_TIMEOUT);
+
+ // RARE is considered idle
+ assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
+ assertFalse(aih.isIdle(PACKAGE_2, USER_ID, 3000));
+
+ // Check persistence
+ aih.writeAppIdleDurations();
+ aih.writeAppIdleTimes(USER_ID);
+ aih = new AppIdleHistory(mStorageDir, 4000);
+ assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
+ assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
}
} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
new file mode 100644
index 000000000000..9846d6f6f346
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.AppStandby.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
+import static android.app.usage.AppStandby.STANDBY_BUCKET_WORKING_SET;
+
+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 static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.usage.AppStandby;
+import android.app.usage.UsageEvents;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test for AppStandbyController.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AppStandbyControllerTests {
+
+ private static final String PACKAGE_1 = "com.example.foo";
+ private static final int UID_1 = 10000;
+ private static final int USER_ID = 0;
+
+ private static final long MINUTE_MS = 60 * 1000;
+ private static final long HOUR_MS = 60 * MINUTE_MS;
+ private static final long DAY_MS = 24 * HOUR_MS;
+
+ private MyInjector mInjector;
+
+ static class MyContextWrapper extends ContextWrapper {
+ PackageManager mockPm = mock(PackageManager.class);
+
+ public MyContextWrapper(Context base) {
+ super(base);
+ }
+
+ public PackageManager getPackageManager() {
+ return mockPm;
+ }
+ }
+
+ static class MyInjector extends AppStandbyController.Injector {
+ long mElapsedRealtime;
+ boolean mIsCharging;
+ List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
+ boolean mDisplayOn;
+ DisplayManager.DisplayListener mDisplayListener;
+ String mBoundWidgetPackage;
+
+ MyInjector(Context context, Looper looper) {
+ super(context, looper);
+ }
+
+ @Override
+ void onBootPhase(int phase) {
+ }
+
+ @Override
+ int getBootPhase() {
+ return SystemService.PHASE_BOOT_COMPLETED;
+ }
+
+ @Override
+ long elapsedRealtime() {
+ return mElapsedRealtime;
+ }
+
+ @Override
+ long currentTimeMillis() {
+ return mElapsedRealtime;
+ }
+
+ @Override
+ boolean isAppIdleEnabled() {
+ return true;
+ }
+
+ @Override
+ boolean isCharging() {
+ return mIsCharging;
+ }
+
+ @Override
+ boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
+ return mPowerSaveWhitelistExceptIdle.contains(packageName);
+ }
+
+ @Override
+ File getDataSystemDirectory() {
+ return new File(getContext().getFilesDir(), Long.toString(Math.randomLongInternal()));
+ }
+
+ @Override
+ void noteEvent(int event, String packageName, int uid) throws RemoteException {
+ }
+
+ @Override
+ boolean isPackageEphemeral(int userId, String packageName) {
+ // TODO: update when testing ephemeral apps scenario
+ return false;
+ }
+
+ @Override
+ int[] getRunningUserIds() {
+ return new int[] {USER_ID};
+ }
+
+ @Override
+ boolean isDefaultDisplayOn() {
+ return mDisplayOn;
+ }
+
+ @Override
+ void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
+ mDisplayListener = listener;
+ }
+
+ @Override
+ String getActiveNetworkScorer() {
+ return null;
+ }
+
+ @Override
+ public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
+ int userId) {
+ return packageName != null && packageName.equals(mBoundWidgetPackage);
+ }
+
+ // Internal methods
+
+ void setDisplayOn(boolean on) {
+ mDisplayOn = on;
+ if (mDisplayListener != null) {
+ mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY);
+ }
+ }
+ }
+
+ private void setupPm(PackageManager mockPm) throws PackageManager.NameNotFoundException {
+ List<PackageInfo> packages = new ArrayList<>();
+ PackageInfo pi = new PackageInfo();
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.uid = UID_1;
+ pi.packageName = PACKAGE_1;
+ packages.add(pi);
+
+ doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
+ try {
+ doReturn(UID_1).when(mockPm).getPackageUidAsUser(anyString(), anyInt(), anyInt());
+ doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(anyString(), anyInt());
+ } catch (PackageManager.NameNotFoundException nnfe) {}
+ }
+
+ private void setChargingState(AppStandbyController controller, boolean charging) {
+ mInjector.mIsCharging = charging;
+ if (controller != null) {
+ controller.setChargingState(charging);
+ }
+ }
+
+ private AppStandbyController setupController() throws Exception {
+ mInjector.mElapsedRealtime = 0;
+ AppStandbyController controller = new AppStandbyController(mInjector);
+ controller.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ mInjector.setDisplayOn(false);
+ mInjector.setDisplayOn(true);
+ setChargingState(controller, false);
+ setupPm(mInjector.getContext().getPackageManager());
+ controller.checkIdleStates(USER_ID);
+
+ return controller;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
+ mInjector = new MyInjector(myContext, Looper.getMainLooper());
+ }
+
+ @Test
+ public void testCharging() throws Exception {
+ AppStandbyController controller = setupController();
+
+ setChargingState(controller, true);
+ mInjector.mElapsedRealtime = 8 * DAY_MS;
+ assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
+ mInjector.mElapsedRealtime, false));
+
+ setChargingState(controller, false);
+ mInjector.mElapsedRealtime = 16 * DAY_MS;
+ controller.checkIdleStates(USER_ID);
+ assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
+ mInjector.mElapsedRealtime, false));
+ setChargingState(controller, true);
+ assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID,
+ mInjector.mElapsedRealtime, false));
+ }
+
+ private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
+ mInjector.mElapsedRealtime = elapsedTime;
+ controller.checkIdleStates(USER_ID);
+ assertEquals(bucket,
+ controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+ false));
+ }
+
+ @Test
+ public void testBuckets() throws Exception {
+ AppStandbyController controller = setupController();
+
+ // ACTIVE bucket
+ assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+
+ // WORKING_SET bucket
+ assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+
+ // WORKING_SET bucket
+ assertTimeout(controller, 47 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+
+ // FREQUENT bucket
+ assertTimeout(controller, 4 * DAY_MS, STANDBY_BUCKET_FREQUENT);
+
+ // RARE bucket
+ assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
+
+ // Back to ACTIVE on event
+ UsageEvents.Event ev = new UsageEvents.Event();
+ ev.mPackage = PACKAGE_1;
+ ev.mEventType = UsageEvents.Event.USER_INTERACTION;
+ controller.reportEvent(ev, mInjector.mElapsedRealtime, USER_ID);
+
+ assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
+
+ // RARE bucket
+ assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+ }
+
+ @Test
+ public void testScreenTimeAndBuckets() throws Exception {
+ AppStandbyController controller = setupController();
+ mInjector.setDisplayOn(false);
+
+ // ACTIVE bucket
+ assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+
+ // WORKING_SET bucket
+ assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+
+ // RARE bucket, should fail because the screen wasn't ON.
+ mInjector.mElapsedRealtime = 9 * DAY_MS;
+ controller.checkIdleStates(USER_ID);
+ assertNotEquals(STANDBY_BUCKET_RARE,
+ controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+ false));
+
+ mInjector.setDisplayOn(true);
+ assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+ }
+
+ @Test
+ public void testForcedIdle() throws Exception {
+ AppStandbyController controller = setupController();
+ setChargingState(controller, false);
+
+ controller.forceIdleState(PACKAGE_1, USER_ID, true);
+ assertEquals(STANDBY_BUCKET_RARE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
+ true));
+ assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+
+ controller.forceIdleState(PACKAGE_1, USER_ID, false);
+ assertEquals(STANDBY_BUCKET_ACTIVE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
+ true));
+ assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 56a3fb094280..0980f7ec4df0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -63,7 +63,7 @@ import java.util.LinkedList;
class WindowTestsBase {
static WindowManagerService sWm = null;
private static final IWindow sIWindow = new TestIWindow();
- private static final Session sMockSession = mock(Session.class);
+ private static Session sMockSession;
// The default display is removed in {@link #setUp} and then we iterate over all displays to
// make sure we don't collide with any existing display. If we run into no other display, the
// added display should be treated as default. This cannot be the default display
@@ -89,7 +89,11 @@ class WindowTestsBase {
public void setUp() throws Exception {
if (!sOneTimeSetupDone) {
sOneTimeSetupDone = true;
+
+ // Allows to mock package local classes and methods
+ System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
+ sMockSession = mock(Session.class);
}
final Context context = InstrumentationRegistry.getTargetContext();
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index f2985597573f..e5d3915dde4c 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,6 +16,9 @@
package com.android.server.usage;
+import static android.app.usage.AppStandby.*;
+
+import android.app.usage.AppStandby;
import android.os.Environment;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -51,8 +54,10 @@ public class AppIdleHistory {
private static final String TAG = "AppIdleHistory";
+ private static final boolean DEBUG = AppStandbyController.DEBUG;
+
// History for all users and all packages
- private SparseArray<ArrayMap<String,PackageHistory>> mIdleHistory = new SparseArray<>();
+ private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
private long mLastPeriod = 0;
private static final long ONE_MINUTE = 60 * 1000;
private static final int HISTORY_SIZE = 100;
@@ -70,6 +75,13 @@ public class AppIdleHistory {
private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
// Elapsed timebase time when app was last used
private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
+ private static final String ATTR_CURRENT_BUCKET = "appLimitBucket";
+ private static final String ATTR_BUCKETING_REASON = "bucketReason";
+
+ // State that was last informed to listeners, since boot
+ private static final int STATE_UNINFORMED = 0;
+ private static final int STATE_ACTIVE = 1;
+ private static final int STATE_IDLE = 2;
// device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -85,17 +97,15 @@ public class AppIdleHistory {
private boolean mScreenOn;
- private static class PackageHistory {
+ private static class AppUsageHistory {
final byte[] recent = new byte[HISTORY_SIZE];
long lastUsedElapsedTime;
long lastUsedScreenTime;
+ @StandbyBuckets int currentBucket;
+ String bucketingReason;
+ int lastInformedState;
}
- AppIdleHistory(long elapsedRealtime) {
- this(Environment.getDataSystemDirectory(), elapsedRealtime);
- }
-
- @VisibleForTesting
AppIdleHistory(File storageDir, long elapsedRealtime) {
mElapsedSnapshot = elapsedRealtime;
mScreenOnSnapshot = elapsedRealtime;
@@ -119,6 +129,9 @@ public class AppIdleHistory {
mElapsedDuration += elapsedRealtime - mElapsedSnapshot;
mElapsedSnapshot = elapsedRealtime;
}
+ if (DEBUG) Slog.d(TAG, "mScreenOnSnapshot=" + mScreenOnSnapshot
+ + ", mScreenOnDuration=" + mScreenOnDuration
+ + ", mScreenOn=" + mScreenOn);
}
public long getScreenOnTime(long elapsedRealtime) {
@@ -174,29 +187,35 @@ public class AppIdleHistory {
}
public void reportUsage(String packageName, int userId, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
- PackageHistory packageHistory = getPackageHistory(userHistory, packageName,
- elapsedRealtime);
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtime, true);
shiftHistoryToNow(userHistory, elapsedRealtime);
- packageHistory.lastUsedElapsedTime = mElapsedDuration
+ appUsageHistory.lastUsedElapsedTime = mElapsedDuration
+ (elapsedRealtime - mElapsedSnapshot);
- packageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
- packageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
+ appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
+ appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
+ appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_ACTIVE;
+ appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+ if (DEBUG) {
+ Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ + ", reason=" + appUsageHistory.bucketingReason);
+ }
}
public void setIdle(String packageName, int userId, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
- PackageHistory packageHistory = getPackageHistory(userHistory, packageName,
- elapsedRealtime);
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtime, true);
shiftHistoryToNow(userHistory, elapsedRealtime);
- packageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
+ appUsageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
}
- private void shiftHistoryToNow(ArrayMap<String, PackageHistory> userHistory,
+ private void shiftHistoryToNow(ArrayMap<String, AppUsageHistory> userHistory,
long elapsedRealtime) {
long thisPeriod = elapsedRealtime / PERIOD_DURATION;
// Has the period switched over? Slide all users' package histories
@@ -206,7 +225,7 @@ public class AppIdleHistory {
final int NUSERS = mIdleHistory.size();
for (int u = 0; u < NUSERS; u++) {
userHistory = mIdleHistory.valueAt(u);
- for (PackageHistory idleState : userHistory.values()) {
+ for (AppUsageHistory idleState : userHistory.values()) {
// Shift left
System.arraycopy(idleState.recent, diff, idleState.recent, 0,
HISTORY_SIZE - diff);
@@ -221,8 +240,8 @@ public class AppIdleHistory {
mLastPeriod = thisPeriod;
}
- private ArrayMap<String, PackageHistory> getUserHistory(int userId) {
- ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+ private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
+ ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
if (userHistory == null) {
userHistory = new ArrayMap<>();
mIdleHistory.put(userId, userHistory);
@@ -231,16 +250,18 @@ public class AppIdleHistory {
return userHistory;
}
- private PackageHistory getPackageHistory(ArrayMap<String, PackageHistory> userHistory,
- String packageName, long elapsedRealtime) {
- PackageHistory packageHistory = userHistory.get(packageName);
- if (packageHistory == null) {
- packageHistory = new PackageHistory();
- packageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
- packageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
- userHistory.put(packageName, packageHistory);
+ private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory,
+ String packageName, long elapsedRealtime, boolean create) {
+ AppUsageHistory appUsageHistory = userHistory.get(packageName);
+ if (appUsageHistory == null && create) {
+ appUsageHistory = new AppUsageHistory();
+ appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
+ appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
+ appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_NEVER;
+ appUsageHistory.bucketingReason = REASON_DEFAULT;
+ userHistory.put(packageName, appUsageHistory);
}
- return packageHistory;
+ return appUsageHistory;
}
public void onUserRemoved(int userId) {
@@ -248,48 +269,124 @@ public class AppIdleHistory {
}
public boolean isIdle(String packageName, int userId, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
- PackageHistory packageHistory =
- getPackageHistory(userHistory, packageName, elapsedRealtime);
- if (packageHistory == null) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+ if (appUsageHistory == null) {
return false; // Default to not idle
} else {
- return hasPassedThresholds(packageHistory, elapsedRealtime);
+ return appUsageHistory.currentBucket >= AppStandby.STANDBY_BUCKET_RARE;
+ // Whether or not it's passed will now be externally calculated and the
+ // bucket will be pushed to the history using setAppStandbyBucket()
+ //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
}
}
+ public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
+ int bucket, String reason) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+ appUsageHistory.currentBucket = bucket;
+ appUsageHistory.bucketingReason = reason;
+ if (DEBUG) {
+ Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ + ", reason=" + appUsageHistory.bucketingReason);
+ }
+ }
+
+ public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+ return appUsageHistory.currentBucket;
+ }
+
+ public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, false);
+ return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
+ }
+
private long getElapsedTime(long elapsedRealtime) {
return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
}
public void setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
- PackageHistory packageHistory = getPackageHistory(userHistory, packageName,
- elapsedRealtime);
- packageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime)
- - mElapsedTimeThreshold;
- packageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime)
- - (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */;
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtime, true);
+ if (idle) {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
+ appUsageHistory.bucketingReason = REASON_FORCED;
+ } else {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+ // This is to pretend that the app was just used, don't freeze the state anymore.
+ appUsageHistory.bucketingReason = REASON_USAGE;
+ }
}
public void clearUsage(String packageName, int userId) {
- ArrayMap<String, PackageHistory> userHistory = getUserHistory(userId);
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
userHistory.remove(packageName);
}
- private boolean hasPassedThresholds(PackageHistory packageHistory, long elapsedRealtime) {
- return (packageHistory.lastUsedScreenTime
- <= getScreenOnTime(elapsedRealtime) - mScreenOnTimeThreshold)
- && (packageHistory.lastUsedElapsedTime
- <= getElapsedTime(elapsedRealtime) - mElapsedTimeThreshold);
+ boolean shouldInformListeners(String packageName, int userId,
+ long elapsedRealtime, boolean isIdle) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtime, true);
+ int targetState = isIdle? STATE_IDLE : STATE_ACTIVE;
+ if (appUsageHistory.lastInformedState != (isIdle ? STATE_IDLE : STATE_ACTIVE)) {
+ appUsageHistory.lastInformedState = targetState;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds
+ * that corresponds to how long since the app was used.
+ * @param packageName
+ * @param userId
+ * @param elapsedRealtime current time
+ * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0
+ * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0
+ * @return The index whose values the app's used time exceeds (in both arrays)
+ */
+ int getThresholdIndex(String packageName, int userId, long elapsedRealtime,
+ long[] screenTimeThresholds, long[] elapsedTimeThresholds) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtime, false);
+ // If we don't have any state for the app, assume never used
+ if (appUsageHistory == null) return screenTimeThresholds.length - 1;
+
+ long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime;
+ long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime;
+
+ if (DEBUG) Slog.d(TAG, packageName
+ + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime
+ + " lastUsedElapsed=" + appUsageHistory.lastUsedElapsedTime);
+ if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta
+ + ", elapsed=" + elapsedDelta);
+ for (int i = screenTimeThresholds.length - 1; i >= 0; i--) {
+ if (screenOnDelta >= screenTimeThresholds[i]
+ && elapsedDelta >= elapsedTimeThresholds[i]) {
+ return i;
+ }
+ }
+ return 0;
}
- private File getUserFile(int userId) {
+ @VisibleForTesting
+ File getUserFile(int userId) {
return new File(new File(new File(mStorageDir, "users"),
Integer.toString(userId)), APP_IDLE_FILENAME);
}
- private void readAppIdleTimes(int userId, ArrayMap<String, PackageHistory> userHistory) {
+ private void readAppIdleTimes(int userId, ArrayMap<String, AppUsageHistory> userHistory) {
FileInputStream fis = null;
try {
AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
@@ -315,12 +412,22 @@ public class AppIdleHistory {
final String name = parser.getName();
if (name.equals(TAG_PACKAGE)) {
final String packageName = parser.getAttributeValue(null, ATTR_NAME);
- PackageHistory packageHistory = new PackageHistory();
- packageHistory.lastUsedElapsedTime =
+ AppUsageHistory appUsageHistory = new AppUsageHistory();
+ appUsageHistory.lastUsedElapsedTime =
Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
- packageHistory.lastUsedScreenTime =
+ appUsageHistory.lastUsedScreenTime =
Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
- userHistory.put(packageName, packageHistory);
+ String currentBucketString = parser.getAttributeValue(null,
+ ATTR_CURRENT_BUCKET);
+ appUsageHistory.currentBucket = currentBucketString == null
+ ? AppStandby.STANDBY_BUCKET_ACTIVE
+ : Integer.parseInt(currentBucketString);
+ appUsageHistory.bucketingReason =
+ parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
+ if (appUsageHistory.bucketingReason == null) {
+ appUsageHistory.bucketingReason = REASON_DEFAULT;
+ }
+ userHistory.put(packageName, appUsageHistory);
}
}
}
@@ -345,17 +452,20 @@ public class AppIdleHistory {
xml.startTag(null, TAG_PACKAGES);
- ArrayMap<String,PackageHistory> userHistory = getUserHistory(userId);
+ ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId);
final int N = userHistory.size();
for (int i = 0; i < N; i++) {
String packageName = userHistory.keyAt(i);
- PackageHistory history = userHistory.valueAt(i);
+ AppUsageHistory history = userHistory.valueAt(i);
xml.startTag(null, TAG_PACKAGE);
xml.attribute(null, ATTR_NAME, packageName);
xml.attribute(null, ATTR_ELAPSED_IDLE,
Long.toString(history.lastUsedElapsedTime));
xml.attribute(null, ATTR_SCREEN_IDLE,
Long.toString(history.lastUsedScreenTime));
+ xml.attribute(null, ATTR_CURRENT_BUCKET,
+ Integer.toString(history.currentBucket));
+ xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
xml.endTag(null, TAG_PACKAGE);
}
@@ -368,10 +478,10 @@ public class AppIdleHistory {
}
}
- public void dump(IndentingPrintWriter idpw, int userId) {
+ public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
idpw.println("Package idle stats:");
idpw.increaseIndent();
- ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+ ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long totalElapsedTime = getElapsedTime(elapsedRealtime);
final long screenOnTime = getScreenOnTime(elapsedRealtime);
@@ -379,13 +489,18 @@ public class AppIdleHistory {
final int P = userHistory.size();
for (int p = 0; p < P; p++) {
final String packageName = userHistory.keyAt(p);
- final PackageHistory packageHistory = userHistory.valueAt(p);
+ final AppUsageHistory appUsageHistory = userHistory.valueAt(p);
+ if (pkg != null && !pkg.equals(packageName)) {
+ continue;
+ }
idpw.print("package=" + packageName);
idpw.print(" lastUsedElapsed=");
- TimeUtils.formatDuration(totalElapsedTime - packageHistory.lastUsedElapsedTime, idpw);
+ TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
idpw.print(" lastUsedScreenOn=");
- TimeUtils.formatDuration(screenOnTime - packageHistory.lastUsedScreenTime, idpw);
+ TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
+ idpw.print(" bucket=" + appUsageHistory.currentBucket
+ + " reason=" + appUsageHistory.bucketingReason);
idpw.println();
}
idpw.println();
@@ -399,7 +514,7 @@ public class AppIdleHistory {
}
public void dumpHistory(IndentingPrintWriter idpw, int userId) {
- ArrayMap<String, PackageHistory> userHistory = mIdleHistory.get(userId);
+ ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
if (userHistory == null) return;
final int P = userHistory.size();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index b2446ba7158d..17fde57907d7 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -18,13 +18,14 @@ package com.android.server.usage;
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
-import static com.android.server.usage.UsageStatsService.MSG_REPORT_EVENT;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
+import android.app.usage.AppStandby;
+import android.app.usage.AppStandby.StandbyBuckets;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -41,6 +42,7 @@ import android.hardware.display.DisplayManager;
import android.net.NetworkScoreManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
+import android.os.Environment;
import android.os.Handler;
import android.os.IDeviceIdleController;
import android.os.Looper;
@@ -66,8 +68,10 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
+import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -76,10 +80,33 @@ import java.util.List;
public class AppStandbyController {
private static final String TAG = "AppStandbyController";
- private static final boolean DEBUG = false;
+ static final boolean DEBUG = false;
static final boolean COMPRESS_TIME = false;
private static final long ONE_MINUTE = 60 * 1000;
+ private static final long ONE_HOUR = ONE_MINUTE * 60;
+ private static final long ONE_DAY = ONE_HOUR * 24;
+
+ static final long[] SCREEN_TIME_THRESHOLDS = {
+ 0,
+ 0,
+ COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
+ COMPRESS_TIME ? 240 * 1000 : 8 * ONE_HOUR
+ };
+
+ static final long[] ELAPSED_TIME_THRESHOLDS = {
+ 0,
+ COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
+ COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_DAY,
+ COMPRESS_TIME ? 16 * ONE_MINUTE : 8 * ONE_DAY
+ };
+
+ static final int[] THRESHOLD_BUCKETS = {
+ AppStandby.STANDBY_BUCKET_ACTIVE,
+ AppStandby.STANDBY_BUCKET_WORKING_SET,
+ AppStandby.STANDBY_BUCKET_FREQUENT,
+ AppStandby.STANDBY_BUCKET_RARE
+ };
// To name the lock for stack traces
static class Lock {}
@@ -92,7 +119,7 @@ public class AppStandbyController {
private AppIdleHistory mAppIdleHistory;
@GuardedBy("mAppIdleLock")
- private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
+ private ArrayList<AppIdleStateChangeListener>
mPackageAccessListeners = new ArrayList<>();
/** Whether we've queried the list of carrier privileged apps. */
@@ -118,6 +145,9 @@ public class AppStandbyController {
long mAppIdleWallclockThresholdMillis;
long mAppIdleParoleIntervalMillis;
long mAppIdleParoleDurationMillis;
+ long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
+ long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
+
boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
boolean mCharging;
@@ -129,20 +159,26 @@ public class AppStandbyController {
private final Handler mHandler;
private final Context mContext;
- private DisplayManager mDisplayManager;
- private IDeviceIdleController mDeviceIdleController;
+ // TODO: Provide a mechanism to set an external bucketing service
+ private boolean mUseInternalBucketingHeuristics = true;
+
private AppWidgetManager mAppWidgetManager;
- private IBatteryStats mBatteryStats;
private PowerManager mPowerManager;
private PackageManager mPackageManager;
- private PackageManagerInternal mPackageManagerInternal;
+ private Injector mInjector;
+
AppStandbyController(Context context, Looper looper) {
- mContext = context;
- mHandler = new AppStandbyHandler(looper);
+ this(new Injector(context, looper));
+ }
+
+ AppStandbyController(Injector injector) {
+ mInjector = injector;
+ mContext = mInjector.getContext();
+ mHandler = new AppStandbyHandler(mInjector.getLooper());
mPackageManager = mContext.getPackageManager();
- mAppIdleEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableAutoPowerModes);
+ mAppIdleEnabled = mInjector.isAppIdleEnabled();
+
if (mAppIdleEnabled) {
IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
@@ -150,7 +186,8 @@ public class AppStandbyController {
mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
}
synchronized (mAppIdleLock) {
- mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
+ mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
+ mInjector.elapsedRealtime());
}
IntentFilter packageFilter = new IntentFilter();
@@ -164,6 +201,7 @@ public class AppStandbyController {
}
public void onBootPhase(int phase) {
+ mInjector.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// Observe changes to the threshold
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
@@ -171,18 +209,11 @@ public class AppStandbyController {
settingsObserver.updateSettings();
mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
- mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mBatteryStats = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
- mDisplayManager = (DisplayManager) mContext.getSystemService(
- Context.DISPLAY_SERVICE);
mPowerManager = mContext.getSystemService(PowerManager.class);
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ mInjector.registerDisplayListener(mDisplayListener, mHandler);
synchronized (mAppIdleLock) {
- mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
+ mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
}
if (mPendingOneTimeCheckIdleStates) {
@@ -191,7 +222,7 @@ public class AppStandbyController {
mSystemServicesReady = true;
} else if (phase == PHASE_BOOT_COMPLETED) {
- setChargingState(mContext.getSystemService(BatteryManager.class).isCharging());
+ setChargingState(mInjector.isCharging());
}
}
@@ -229,7 +260,7 @@ public class AppStandbyController {
/** Paroled here means temporary pardon from being inactive */
void setAppIdleParoled(boolean paroled) {
synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
+ final long now = mInjector.currentTimeMillis();
if (mAppIdleTempParoled != paroled) {
mAppIdleTempParoled = paroled;
if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
@@ -284,7 +315,7 @@ public class AppStandbyController {
* scheduling a series of repeating checkIdleStates each time we fired off one.
*/
void postOneTimeCheckIdleStates() {
- if (mDeviceIdleController == null) {
+ if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
// Not booted yet; wait for it!
mPendingOneTimeCheckIdleStates = true;
} else {
@@ -304,7 +335,7 @@ public class AppStandbyController {
final int[] runningUserIds;
try {
- runningUserIds = ActivityManager.getService().getRunningUserIds();
+ runningUserIds = mInjector.getRunningUserIds();
if (checkUserId != UserHandle.USER_ALL
&& !ArrayUtils.contains(runningUserIds, checkUserId)) {
return false;
@@ -313,7 +344,7 @@ public class AppStandbyController {
throw re.rethrowFromSystemServer();
}
- final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long elapsedRealtime = mInjector.elapsedRealtime();
for (int i = 0; i < runningUserIds.length; i++) {
final int userId = runningUserIds[i];
if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
@@ -329,30 +360,71 @@ public class AppStandbyController {
for (int p = 0; p < packageCount; p++) {
final PackageInfo pi = packages.get(p);
final String packageName = pi.packageName;
- final boolean isIdle = isAppIdleFiltered(packageName,
+ final boolean isSpecial = isAppSpecial(packageName,
UserHandle.getAppId(pi.applicationInfo.uid),
- userId, elapsedRealtime);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- userId, isIdle ? 1 : 0, packageName));
- if (isIdle) {
+ userId);
+ if (DEBUG) {
+ Slog.d(TAG, " Checking idle state for " + packageName);
+ }
+ if (isSpecial) {
+ maybeInformListeners(packageName, userId, elapsedRealtime, false);
+ } else if (mUseInternalBucketingHeuristics) {
synchronized (mAppIdleLock) {
- mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+ int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
+ elapsedRealtime);
+ String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
+ userId, elapsedRealtime);
+ if (bucketingReason != null
+ && (bucketingReason.equals(AppStandby.REASON_FORCED)
+ || bucketingReason.startsWith(AppStandby.REASON_PREDICTED))) {
+ continue;
+ }
+ int newBucket = getBucketForLocked(packageName, userId,
+ elapsedRealtime);
+ if (DEBUG) {
+ Slog.d(TAG, " Old bucket=" + oldBucket
+ + ", newBucket=" + newBucket);
+ }
+ if (oldBucket != newBucket) {
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId,
+ elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ newBucket >= AppStandby.STANDBY_BUCKET_RARE);
+ }
}
}
}
}
if (DEBUG) {
Slog.d(TAG, "checkIdleStates took "
- + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ + (mInjector.elapsedRealtime() - elapsedRealtime));
}
return true;
}
+ private void maybeInformListeners(String packageName, int userId,
+ long elapsedRealtime, boolean isIdle) {
+ synchronized (mAppIdleLock) {
+ if (mAppIdleHistory.shouldInformListeners(packageName, userId,
+ elapsedRealtime, isIdle)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
+ userId, isIdle ? 1 : 0, packageName));
+ }
+ }
+ }
+
+ @StandbyBuckets int getBucketForLocked(String packageName, int userId,
+ long elapsedRealtime) {
+ int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
+ elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
+ return THRESHOLD_BUCKETS[bucketIndex];
+ }
+
/** Check if it's been a while since last parole and let idle apps do some work */
void checkParoleTimeout() {
boolean setParoled = false;
synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
+ final long now = mInjector.currentTimeMillis();
if (!mAppIdleTempParoled) {
final long timeSinceLastParole = now - mLastAppIdleParoledTime;
if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
@@ -374,10 +446,10 @@ public class AppStandbyController {
final int uid = mPackageManager.getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (idle) {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
+ mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
packageName, uid);
} else {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
+ mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
packageName, uid);
}
} catch (PackageManager.NameNotFoundException | RemoteException e) {
@@ -389,7 +461,7 @@ public class AppStandbyController {
if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
boolean paroled = false;
synchronized (mAppIdleLock) {
- final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
+ final long timeSinceLastParole = mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
if (!deviceIdle
&& timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
if (DEBUG) {
@@ -419,13 +491,11 @@ public class AppStandbyController {
|| event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
if (previouslyIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ 0, event.mPackage));
+ maybeInformListeners(event.mPackage, userId, elapsedRealtime, false);
notifyBatteryStats(event.mPackage, userId, false);
}
}
}
-
}
/**
@@ -439,7 +509,7 @@ public class AppStandbyController {
void forceIdleState(String packageName, int userId, boolean idle) {
final int appId = getAppId(packageName);
if (appId < 0) return;
- final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long elapsedRealtime = mInjector.elapsedRealtime();
final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
userId, elapsedRealtime);
@@ -470,7 +540,7 @@ public class AppStandbyController {
}
}
- void addListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ void addListener(AppIdleStateChangeListener listener) {
synchronized (mAppIdleLock) {
if (!mPackageAccessListeners.contains(listener)) {
mPackageAccessListeners.add(listener);
@@ -478,7 +548,7 @@ public class AppStandbyController {
}
}
- void removeListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ void removeListener(AppIdleStateChangeListener listener) {
synchronized (mAppIdleLock) {
mPackageAccessListeners.remove(listener);
}
@@ -501,75 +571,79 @@ public class AppStandbyController {
return false;
}
if (shouldObfuscateInstantApps &&
- mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
+ mInjector.isPackageEphemeral(userId, packageName)) {
return false;
}
return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
}
- /**
- * Checks if an app has been idle for a while and filters out apps that are excluded.
- * It returns false if the current system state allows all apps to be considered active.
- * This happens if the device is plugged in or temporarily allowed to make exceptions.
- * Called by interface impls.
- */
- boolean isAppIdleFiltered(String packageName, int appId, int userId,
- long elapsedRealtime) {
+ /** Returns true if this app should be whitelisted for some reason, to never go into standby */
+ boolean isAppSpecial(String packageName, int appId, int userId) {
if (packageName == null) return false;
// If not enabled at all, of course nobody is ever idle.
if (!mAppIdleEnabled) {
- return false;
+ return true;
}
if (appId < Process.FIRST_APPLICATION_UID) {
// System uids never go idle.
- return false;
+ return true;
}
if (packageName.equals("android")) {
// Nor does the framework (which should be redundant with the above, but for MR1 we will
// retain this for safety).
- return false;
+ return true;
}
if (mSystemServicesReady) {
try {
// We allow all whitelisted apps, including those that don't want to be whitelisted
// for idle mode, because app idle (aka app standby) is really not as big an issue
// for controlling who participates vs. doze mode.
- if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
- return false;
+ if (mInjector.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+ return true;
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
if (isActiveDeviceAdmin(packageName, userId)) {
- return false;
+ return true;
}
if (isActiveNetworkScorer(packageName)) {
- return false;
+ return true;
}
if (mAppWidgetManager != null
- && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
- return false;
+ && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
+ return true;
}
if (isDeviceProvisioningPackage(packageName)) {
- return false;
+ return true;
}
}
- if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
- return false;
+ // Check this last, as it can be the most expensive check
+ if (isCarrierApp(packageName)) {
+ return true;
}
- // Check this last, as it is the most expensive check
- // TODO: Optimize this by fetching the carrier privileged apps ahead of time
- if (isCarrierApp(packageName)) {
+ return false;
+ }
+
+ /**
+ * Checks if an app has been idle for a while and filters out apps that are excluded.
+ * It returns false if the current system state allows all apps to be considered active.
+ * This happens if the device is plugged in or temporarily allowed to make exceptions.
+ * Called by interface impls.
+ */
+ boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ long elapsedRealtime) {
+ if (isAppSpecial(packageName, appId, userId)) {
return false;
+ } else {
+ return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
}
-
- return true;
}
int[] getIdleUidsForUser(int userId) {
@@ -577,7 +651,7 @@ public class AppStandbyController {
return new int[0];
}
- final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long elapsedRealtime = mInjector.elapsedRealtime();
List<ApplicationInfo> apps;
try {
@@ -613,7 +687,7 @@ public class AppStandbyController {
}
}
if (DEBUG) {
- Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
}
int numIdle = 0;
for (int i = uidStates.size() - 1; i >= 0; i--) {
@@ -643,6 +717,21 @@ public class AppStandbyController {
.sendToTarget();
}
+ @StandbyBuckets int getAppStandbyBucket(String packageName, int userId,
+ long elapsedRealtime, boolean shouldObfuscateInstantApps) {
+ if (shouldObfuscateInstantApps &&
+ mInjector.isPackageEphemeral(userId, packageName)) {
+ return AppStandby.STANDBY_BUCKET_ACTIVE;
+ }
+
+ return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
+ }
+
+ void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
+ String reason, long elapsedRealtime) {
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason);
+ }
+
private boolean isActiveDeviceAdmin(String packageName, int userId) {
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) return false;
@@ -662,7 +751,7 @@ public class AppStandbyController {
private boolean isCarrierApp(String packageName) {
synchronized (mAppIdleLock) {
if (!mHaveCarrierPrivilegedApps) {
- fetchCarrierPrivilegedAppsLA();
+ fetchCarrierPrivilegedAppsLocked();
}
if (mCarrierPrivilegedApps != null) {
return mCarrierPrivilegedApps.contains(packageName);
@@ -682,7 +771,7 @@ public class AppStandbyController {
}
@GuardedBy("mAppIdleLock")
- private void fetchCarrierPrivilegedAppsLA() {
+ private void fetchCarrierPrivilegedAppsLocked() {
TelephonyManager telephonyManager =
mContext.getSystemService(TelephonyManager.class);
mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
@@ -693,20 +782,19 @@ public class AppStandbyController {
}
private boolean isActiveNetworkScorer(String packageName) {
- NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
- Context.NETWORK_SCORE_SERVICE);
- return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
+ String activeScorer = mInjector.getActiveNetworkScorer();
+ return packageName != null && packageName.equals(activeScorer);
}
void informListeners(String packageName, int userId, boolean isIdle) {
- for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
listener.onAppIdleStateChanged(packageName, userId, isIdle);
}
}
void informParoleStateChanged() {
final boolean paroled = isParoledOrCharging();
- for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
listener.onParoleStateChanged(paroled);
}
}
@@ -726,8 +814,7 @@ public class AppStandbyController {
}
boolean isDisplayOn() {
- return mDisplayManager
- .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+ return mInjector.isDefaultDisplayOn();
}
void clearAppIdleForPackage(String packageName, int userId) {
@@ -755,7 +842,7 @@ public class AppStandbyController {
void initializeDefaultsForSystemApps(int userId) {
Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long elapsedRealtime = mInjector.elapsedRealtime();
List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
PackageManager.MATCH_DISABLED_COMPONENTS,
userId);
@@ -786,9 +873,9 @@ public class AppStandbyController {
}
}
- void dumpUser(IndentingPrintWriter idpw, int userId) {
+ void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
synchronized (mAppIdleLock) {
- mAppIdleHistory.dump(idpw, userId);
+ mAppIdleHistory.dump(idpw, userId, pkg);
}
}
@@ -828,6 +915,116 @@ public class AppStandbyController {
pw.print(" mLastAppIdleParoledTime=");
TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
pw.println();
+ pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
+ pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
+ }
+
+ /**
+ * Injector for interaction with external code. Override methods to provide a mock
+ * implementation for tests.
+ * onBootPhase() must be called with at least the PHASE_SYSTEM_SERVICES_READY
+ */
+ static class Injector {
+
+ private final Context mContext;
+ private final Looper mLooper;
+ private IDeviceIdleController mDeviceIdleController;
+ private IBatteryStats mBatteryStats;
+ private PackageManagerInternal mPackageManagerInternal;
+ private DisplayManager mDisplayManager;
+ int mBootPhase;
+
+ Injector(Context context, Looper looper) {
+ mContext = context;
+ mLooper = looper;
+ }
+
+ Context getContext() {
+ return mContext;
+ }
+
+ Looper getLooper() {
+ return mLooper;
+ }
+
+ void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ mBatteryStats = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mDisplayManager = (DisplayManager) mContext.getSystemService(
+ Context.DISPLAY_SERVICE);
+ }
+ mBootPhase = phase;
+ }
+
+ int getBootPhase() {
+ return mBootPhase;
+ }
+
+ /**
+ * Returns the elapsed realtime since the device started. Override this
+ * to control the clock.
+ * @return elapsed realtime
+ */
+ long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ boolean isAppIdleEnabled() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableAutoPowerModes);
+ }
+
+ boolean isCharging() {
+ return mContext.getSystemService(BatteryManager.class).isCharging();
+ }
+
+ boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
+ return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName);
+ }
+
+ File getDataSystemDirectory() {
+ return Environment.getDataSystemDirectory();
+ }
+
+ void noteEvent(int event, String packageName, int uid) throws RemoteException {
+ mBatteryStats.noteEvent(event, packageName, uid);
+ }
+
+ boolean isPackageEphemeral(int userId, String packageName) {
+ return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
+ }
+
+ int[] getRunningUserIds() throws RemoteException {
+ return ActivityManager.getService().getRunningUserIds();
+ }
+
+ boolean isDefaultDisplayOn() {
+ return mDisplayManager
+ .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+ }
+
+ void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
+ mDisplayManager.registerDisplayListener(listener, handler);
+ }
+
+ String getActiveNetworkScorer() {
+ NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
+ Context.NETWORK_SCORE_SERVICE);
+ return nsm.getActiveScorerPackage();
+ }
+
+ public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
+ int userId) {
+ return appWidgetManager.isBoundWidgetPackage(packageName, userId);
+ }
}
class AppStandbyHandler extends Handler {
@@ -839,6 +1036,10 @@ public class AppStandbyController {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_INFORM_LISTENERS:
+ informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
case MSG_FORCE_IDLE_STATE:
forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
break;
@@ -911,7 +1112,7 @@ public class AppStandbyController {
if (displayId == Display.DEFAULT_DISPLAY) {
final boolean displayOn = isDisplayOn();
synchronized (mAppIdleLock) {
- mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
+ mAppIdleHistory.updateDisplay(displayOn, mInjector.elapsedRealtime());
}
}
}
@@ -931,6 +1132,8 @@ public class AppStandbyController {
private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
private static final String KEY_PAROLE_INTERVAL = "parole_interval";
private static final String KEY_PAROLE_DURATION = "parole_duration";
+ private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
+ private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -969,7 +1172,7 @@ public class AppStandbyController {
COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
- COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
+ COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
// Default: 24 hours between paroles
mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
@@ -979,9 +1182,35 @@ public class AppStandbyController {
COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
mAppIdleScreenThresholdMillis);
+
+ String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
+ mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
+ SCREEN_TIME_THRESHOLDS);
+
+ String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null);
+ mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
+ ELAPSED_TIME_THRESHOLDS);
}
}
- }
+ long[] parseLongArray(String values, long[] defaults) {
+ if (values == null) return defaults;
+ if (values.isEmpty()) {
+ // Reset to defaults
+ return defaults;
+ } else {
+ String[] thresholds = values.split("/");
+ if (thresholds.length == THRESHOLD_BUCKETS.length) {
+ long[] array = new long[THRESHOLD_BUCKETS.length];
+ for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
+ array[i] = Long.parseLong(thresholds[i]);
+ }
+ return array;
+ } else {
+ return defaults;
+ }
+ }
+ }
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index afafea19afd0..44e6a6cb7459 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IUidObserver;
+import android.app.usage.AppStandby;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
@@ -469,8 +470,32 @@ public class UsageStatsService extends SystemService implements
void dump(String[] args, PrintWriter pw) {
synchronized (mLock) {
IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " ");
- ArraySet<String> argSet = new ArraySet<>();
- argSet.addAll(Arrays.asList(args));
+
+ boolean checkin = false;
+ boolean history = false;
+ String pkg = null;
+
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if ("--checkin".equals(arg)) {
+ checkin = true;
+ } else if ("--history".equals(arg)) {
+ history = true;
+ } else if ("history".equals(arg)) {
+ history = true;
+ break;
+ } else if ("flush".equals(arg)) {
+ flushToDiskLocked();
+ pw.println("Flushed stats to disk");
+ return;
+ } else {
+ // Anything else is a pkg to filter
+ pkg = arg;
+ break;
+ }
+ }
+ }
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
@@ -478,26 +503,23 @@ public class UsageStatsService extends SystemService implements
idpw.printPair("user", userId);
idpw.println();
idpw.increaseIndent();
- if (argSet.contains("--checkin")) {
+ if (checkin) {
mUserState.valueAt(i).checkin(idpw);
} else {
- mUserState.valueAt(i).dump(idpw);
+ mUserState.valueAt(i).dump(idpw, pkg);
idpw.println();
- if (args.length > 0) {
- if ("history".equals(args[0])) {
- mAppStandby.dumpHistory(idpw, userId);
- } else if ("flush".equals(args[0])) {
- flushToDiskLocked();
- pw.println("Flushed stats to disk");
- }
+ if (history) {
+ mAppStandby.dumpHistory(idpw, userId);
}
}
- mAppStandby.dumpUser(idpw, userId);
+ mAppStandby.dumpUser(idpw, userId, pkg);
idpw.decreaseIndent();
}
- pw.println();
- mAppStandby.dumpState(args, pw);
+ if (pkg == null) {
+ pw.println();
+ mAppStandby.dumpState(args, pw);
+ }
}
}
@@ -654,6 +676,55 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public int getAppStandbyBucket(String packageName, String callingPackage, int userId) {
+ if (!hasPermission(callingPackage)) {
+ throw new SecurityException("Don't have permission to query app standby bucket");
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ try {
+ userId = ActivityManager.getService().handleIncomingUser(
+ Binder.getCallingPid(), callingUid, userId, false, true,
+ "getAppStandbyBucket", null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(callingUid,
+ userId);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mAppStandby.getAppStandbyBucket(packageName, userId,
+ SystemClock.elapsedRealtime(), obfuscateInstantApps);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setAppStandbyBucket(String packageName,
+ int bucket, int userId) {
+ getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
+ "No permission to change app standby state");
+
+ final int callingUid = Binder.getCallingUid();
+ try {
+ userId = ActivityManager.getService().handleIncomingUser(
+ Binder.getCallingPid(), callingUid, userId, false, true,
+ "setAppStandbyBucket", null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mAppStandby.setAppStandbyBucket(packageName, userId, bucket,
+ AppStandby.REASON_PREDICTED + ":" + callingUid,
+ SystemClock.elapsedRealtime());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void whitelistAppTemporarily(String packageName, long duration, int userId)
throws RemoteException {
StringBuilder reason = new StringBuilder(32);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 0abbb82a68a1..0b1059045b5b 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -474,19 +474,19 @@ class UserUsageStatsService {
mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
@Override
public boolean checkin(IntervalStats stats) {
- printIntervalStats(pw, stats, false);
+ printIntervalStats(pw, stats, false, null);
return true;
}
});
}
- void dump(IndentingPrintWriter pw) {
+ void dump(IndentingPrintWriter pw, String pkg) {
// This is not a check-in, only dump in-memory stats.
for (int interval = 0; interval < mCurrentStats.length; interval++) {
pw.print("In-memory ");
pw.print(intervalToString(interval));
pw.println(" stats");
- printIntervalStats(pw, mCurrentStats[interval], true);
+ printIntervalStats(pw, mCurrentStats[interval], true, pkg);
}
}
@@ -505,7 +505,7 @@ class UserUsageStatsService {
}
void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
- boolean prettyDates) {
+ boolean prettyDates, String pkg) {
if (prettyDates) {
pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
stats.beginTime, stats.endTime, sDateFormatFlags) + "\"");
@@ -521,6 +521,9 @@ class UserUsageStatsService {
final int pkgCount = pkgStats.size();
for (int i = 0; i < pkgCount; i++) {
final UsageStats usageStats = pkgStats.valueAt(i);
+ if (pkg != null && !pkg.equals(usageStats.mPackageName)) {
+ continue;
+ }
pw.printPair("package", usageStats.mPackageName);
pw.printPair("totalTime",
formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
@@ -533,6 +536,9 @@ class UserUsageStatsService {
pw.println("ChooserCounts");
pw.increaseIndent();
for (UsageStats usageStats : pkgStats.values()) {
+ if (pkg != null && !pkg.equals(usageStats.mPackageName)) {
+ continue;
+ }
pw.printPair("package", usageStats.mPackageName);
if (usageStats.mChooserCounts != null) {
final int chooserCountSize = usageStats.mChooserCounts.size();
@@ -555,19 +561,22 @@ class UserUsageStatsService {
}
pw.decreaseIndent();
- pw.println("configurations");
- pw.increaseIndent();
- final ArrayMap<Configuration, ConfigurationStats> configStats = stats.configurations;
- final int configCount = configStats.size();
- for (int i = 0; i < configCount; i++) {
- final ConfigurationStats config = configStats.valueAt(i);
- pw.printPair("config", Configuration.resourceQualifierString(config.mConfiguration));
- pw.printPair("totalTime", formatElapsedTime(config.mTotalTimeActive, prettyDates));
- pw.printPair("lastTime", formatDateTime(config.mLastTimeActive, prettyDates));
- pw.printPair("count", config.mActivationCount);
- pw.println();
+ if (pkg == null) {
+ pw.println("configurations");
+ pw.increaseIndent();
+ final ArrayMap<Configuration, ConfigurationStats> configStats = stats.configurations;
+ final int configCount = configStats.size();
+ for (int i = 0; i < configCount; i++) {
+ final ConfigurationStats config = configStats.valueAt(i);
+ pw.printPair("config", Configuration.resourceQualifierString(
+ config.mConfiguration));
+ pw.printPair("totalTime", formatElapsedTime(config.mTotalTimeActive, prettyDates));
+ pw.printPair("lastTime", formatDateTime(config.mLastTimeActive, prettyDates));
+ pw.printPair("count", config.mActivationCount);
+ pw.println();
+ }
+ pw.decreaseIndent();
}
- pw.decreaseIndent();
pw.println("events");
pw.increaseIndent();
@@ -575,6 +584,9 @@ class UserUsageStatsService {
final int eventCount = events != null ? events.size() : 0;
for (int i = 0; i < eventCount; i++) {
final UsageEvents.Event event = events.valueAt(i);
+ if (pkg != null && !pkg.equals(event.mPackage)) {
+ continue;
+ }
pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
pw.printPair("type", eventToString(event.mEventType));
pw.printPair("package", event.mPackage);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index de980b2ffec2..47b0f79b29fc 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1419,6 +1419,17 @@ public class CarrierConfigManager {
"support_3gpp_call_forwarding_while_roaming_bool";
/**
+ * Boolean indicating whether to display voicemail number as default call forwarding number in
+ * call forwarding settings.
+ * If true, display vm number when cf number is null.
+ * If false, display the cf number from network.
+ * By default this value is false.
+ * @hide
+ */
+ public static final String KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL =
+ "display_voicemail_number_as_default_call_forwarding_number";
+
+ /**
* When {@code true}, the user will be notified when they attempt to place an international call
* when the call is placed using wifi calling.
* @hide
@@ -1604,6 +1615,13 @@ public class CarrierConfigManager {
public static final String KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL =
"skip_cf_fail_to_disable_dialog_bool";
+ /**
+ * List of the FAC (feature access codes) to dial as a normal call.
+ * @hide
+ */
+ public static final String KEY_FEATURE_ACCESS_CODES_STRING_ARRAY =
+ "feature_access_codes_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1860,6 +1878,8 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+ sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
+ false);
sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
null);
@@ -1874,6 +1894,7 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
+ sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
}
/**
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 98fb65343485..c3a2ceb1a344 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -273,6 +273,13 @@ public class DisconnectCause {
* {@hide}
*/
public static final int EMERGENCY_PERM_FAILURE = 64;
+
+ /**
+ * This cause is used to report a normal event only when no other cause in the normal class
+ * applies.
+ * {@hide}
+ */
+ public static final int NORMAL_UNSPECIFIED = 65;
//*********************************************************************************************
// When adding a disconnect type:
// 1) Update toString() with the newly added disconnect type.
@@ -413,6 +420,8 @@ public class DisconnectCause {
return "EMERGENCY_TEMP_FAILURE";
case EMERGENCY_PERM_FAILURE:
return "EMERGENCY_PERM_FAILURE";
+ case NORMAL_UNSPECIFIED:
+ return "NORMAL_UNSPECIFIED";
default:
return "INVALID: " + cause;
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 6029995f2468..98195ada16f0 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -390,20 +390,23 @@ public final class SmsManager {
* Inject an SMS PDU into the android application framework.
*
* <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
- * privileges. @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ * privileges per {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
*
* @param pdu is the byte array of pdu to be injected into android application framework
- * @param format is the format of SMS pdu (3gpp or 3gpp2)
+ * @param format is the format of SMS pdu ({@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2})
* @param receivedIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is successfully received by the
* android application framework, or failed. This intent is broadcasted at
* the same time an SMS received from radio is acknowledged back.
- * The result code will be <code>RESULT_SMS_HANDLED</code> for success, or
- * <code>RESULT_SMS_GENERIC_ERROR</code> for error.
+ * The result code will be {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_HANDLED}
+ * for success, or {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_GENERIC_ERROR} for
+ * error.
*
- * @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2.
+ * @throws IllegalArgumentException if the format is invalid.
*/
- public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
+ public void injectSmsPdu(
+ byte[] pdu, @SmsMessage.Format String format, PendingIntent receivedIntent) {
if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
// Format must be either 3gpp or 3gpp2.
throw new IllegalArgumentException(
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index dcdda8685e02..df41233559a1 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -16,24 +16,25 @@
package android.telephony;
-import android.os.Binder;
-import android.os.Parcel;
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+import android.annotation.StringDef;
import android.content.res.Resources;
+import android.os.Binder;
import android.text.TextUtils;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
-import java.lang.Math;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
-import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
-
/**
* A Short Message Service message.
@@ -81,15 +82,18 @@ public class SmsMessage {
*/
public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+ /** @hide */
+ @StringDef({FORMAT_3GPP, FORMAT_3GPP2})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Format {}
+
/**
* Indicates a 3GPP format SMS message.
- * @hide pending API council approval
*/
public static final String FORMAT_3GPP = "3gpp";
/**
* Indicates a 3GPP2 format SMS message.
- * @hide pending API council approval
*/
public static final String FORMAT_3GPP2 = "3gpp2";
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 060a51895474..29a95e65dbf3 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -106,9 +106,11 @@ LOCAL_JAVA_LIBRARIES := \
android.test.mock.stubs \
LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SDK_VERSION := current
# Make sure to run droiddoc first to generate the stub source files.
LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_runner_api_gen_stamp)
+android_test_runner_api_gen_stamp :=
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -203,6 +205,7 @@ LOCAL_SOURCE_FILES_ALL_GENERATED := true
# Make sure to run droiddoc first to generate the stub source files.
LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
+android_test_mock_gen_stamp :=
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -246,6 +249,7 @@ update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP)
include $(CLEAR_VARS)
LOCAL_MODULE := android.test.mock.sdk
+LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android.test.mock.stubs
diff --git a/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java b/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java
new file mode 100644
index 000000000000..39f59f1c0d69
--- /dev/null
+++ b/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.reset;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VersionedBroadcastListenerTest {
+ private static final String TAG = VersionedBroadcastListenerTest.class.getSimpleName();
+ private static final String ACTION_TEST = "action.test.happy.broadcasts";
+
+ @Mock private Context mContext;
+ private BroadcastInterceptingContext mServiceContext;
+ private Handler mHandler;
+ private VersionedBroadcastListener mListener;
+ private int mCallbackCount;
+
+ private void doCallback() { mCallbackCount++; }
+
+ private class MockContext extends BroadcastInterceptingContext {
+ MockContext(Context base) {
+ super(base);
+ }
+ }
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ @Before public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ reset(mContext);
+ mServiceContext = new MockContext(mContext);
+ mHandler = new Handler(Looper.myLooper());
+ mCallbackCount = 0;
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_TEST);
+ mListener = new VersionedBroadcastListener(
+ TAG, mServiceContext, mHandler, filter, (Intent intent) -> doCallback());
+ }
+
+ @After public void tearDown() throws Exception {
+ if (mListener != null) {
+ mListener.stopListening();
+ mListener = null;
+ }
+ }
+
+ private void sendBroadcast() {
+ final Intent intent = new Intent(ACTION_TEST);
+ mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ @Test
+ public void testBasicListening() {
+ assertEquals(0, mCallbackCount);
+ mListener.startListening();
+ for (int i = 0; i < 5; i++) {
+ sendBroadcast();
+ assertEquals(i+1, mCallbackCount);
+ }
+ mListener.stopListening();
+ }
+
+ @Test
+ public void testBroadcastsBeforeStartAreIgnored() {
+ assertEquals(0, mCallbackCount);
+ for (int i = 0; i < 5; i++) {
+ sendBroadcast();
+ assertEquals(0, mCallbackCount);
+ }
+
+ mListener.startListening();
+ sendBroadcast();
+ assertEquals(1, mCallbackCount);
+ }
+
+ @Test
+ public void testBroadcastsAfterStopAreIgnored() {
+ mListener.startListening();
+ sendBroadcast();
+ assertEquals(1, mCallbackCount);
+ mListener.stopListening();
+
+ for (int i = 0; i < 5; i++) {
+ sendBroadcast();
+ assertEquals(1, mCallbackCount);
+ }
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c2cb66d5e60a..27a29b68e12d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -104,6 +104,8 @@ import android.util.LogPrinter;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.DefaultNetworkMetrics;
+import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
@@ -156,6 +158,9 @@ public class ConnectivityServiceTest {
private MockNetworkAgent mEthernetNetworkAgent;
private Context mContext;
+ @Mock IpConnectivityMetrics.Logger mMetricsService;
+ @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+
// This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
// do not go through ConnectivityService but talk to netd directly, so they don't automatically
// reflect the state of our test ConnectivityService.
@@ -805,6 +810,11 @@ public class ConnectivityServiceTest {
return Context.ETHERNET_SERVICE.equals(name);
}
+ @Override
+ protected IpConnectivityMetrics.Logger metricsLogger() {
+ return mMetricsService;
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -833,6 +843,9 @@ public class ConnectivityServiceTest {
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
+ MockitoAnnotations.initMocks(this);
+ when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
+
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
if (Looper.myLooper() == null) {
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 83ee361aa2e3..7b0703851441 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -21,6 +21,7 @@ import static android.system.OsConstants.EADDRINUSE;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
@@ -174,6 +175,7 @@ public class IpSecServiceTest {
mIpSecService.openUdpEncapsulationSocket(0, new Binder());
assertNotNull(udpEncapResp);
assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
+ assertNotEquals(0, udpEncapResp.port);
mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
udpEncapResp.fileDescriptor.close();
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 262417620ca2..ad6ebf933776 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -198,21 +198,20 @@ public class IpConnectivityEventBuilderTest {
@Test
public void testDefaultNetworkEventSerialization() {
- ConnectivityMetricsEvent ev = describeIpEvent(
- aType(DefaultNetworkEvent.class),
- anInt(102),
- anIntArray(1, 2, 3),
- anInt(101),
- aBool(true),
- aBool(false));
+ DefaultNetworkEvent ev = new DefaultNetworkEvent();
+ ev.netId = 102;
+ ev.prevNetId = 101;
+ ev.transportTypes = new int[]{1, 2, 3};
+ ev.prevIPv4 = true;
+ ev.prevIPv6 = true;
String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
" link_layer: 0",
- " network_id: 0",
- " time_ms: 1",
+ " network_id: 102",
+ " time_ms: 0",
" transports: 0",
" default_network_event <",
" default_network_duration_ms: 0",
@@ -226,7 +225,7 @@ public class IpConnectivityEventBuilderTest {
" previous_network_id <",
" network_id: 101",
" >",
- " previous_network_ip_support: 1",
+ " previous_network_ip_support: 3",
" transport_types: 1",
" transport_types: 2",
" transport_types: 3",
@@ -234,7 +233,7 @@ public class IpConnectivityEventBuilderTest {
">",
"version: 2\n");
- verifySerialization(want, ev);
+ verifySerialization(want, IpConnectivityEventBuilder.toProto(ev));
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index a395c480f57a..6c1decc3b3b8 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -18,6 +18,7 @@ package com.android.server.connectivity;
import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,6 +31,10 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.metrics.ApfProgramEvent;
@@ -41,18 +46,22 @@ import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
-import android.system.OsConstants;
import android.os.Parcelable;
import android.support.test.runner.AndroidJUnit4;
+import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
+
+import com.android.internal.util.BitUtils;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -162,6 +171,144 @@ public class IpConnectivityMetricsTest {
}
@Test
+ public void testDefaultNetworkEvents() throws Exception {
+ final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+ final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+
+ NetworkAgentInfo[][] defaultNetworks = {
+ // nothing -> cell
+ {null, makeNai(100, 10, false, true, cell)},
+ // cell -> wifi
+ {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)},
+ // wifi -> nothing
+ {makeNai(101, 60, true, false, wifi), null},
+ // nothing -> cell
+ {null, makeNai(102, 10, true, true, cell)},
+ // cell -> wifi
+ {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
+ };
+
+ for (NetworkAgentInfo[] pair : defaultNetworks) {
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(pair[1], pair[0]);
+ }
+
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 0",
+ " network_id: 100",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " network_id <",
+ " network_id: 100",
+ " >",
+ " no_default_network_duration_ms: 0",
+ " previous_network_id <",
+ " network_id: 0",
+ " >",
+ " previous_network_ip_support: 0",
+ " transport_types: 0",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 0",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " network_id <",
+ " network_id: 101",
+ " >",
+ " no_default_network_duration_ms: 0",
+ " previous_network_id <",
+ " network_id: 100",
+ " >",
+ " previous_network_ip_support: 3",
+ " transport_types: 1",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 0",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " network_id <",
+ " network_id: 0",
+ " >",
+ " no_default_network_duration_ms: 0",
+ " previous_network_id <",
+ " network_id: 101",
+ " >",
+ " previous_network_ip_support: 1",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 0",
+ " network_id: 102",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " network_id <",
+ " network_id: 102",
+ " >",
+ " no_default_network_duration_ms: 0",
+ " previous_network_id <",
+ " network_id: 0",
+ " >",
+ " previous_network_ip_support: 0",
+ " transport_types: 0",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 0",
+ " network_id: 103",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " network_id <",
+ " network_id: 103",
+ " >",
+ " no_default_network_duration_ms: 0",
+ " previous_network_id <",
+ " network_id: 102",
+ " >",
+ " previous_network_ip_support: 3",
+ " transport_types: 1",
+ " >",
+ ">",
+ "version: 2\n");
+
+ verifySerialization(want, getdump("flush"));
+ }
+
+ @Test
public void testEndToEndLogging() throws Exception {
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
@@ -194,7 +341,6 @@ public class IpConnectivityMetricsTest {
Parcelable[] events = {
new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED),
new DhcpClientEvent("SomeState", 192),
- new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false),
new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678),
validationEv,
apfStats,
@@ -233,6 +379,13 @@ public class IpConnectivityMetricsTest {
wakeupEvent("wlan0", 10008);
wakeupEvent("rmnet0", 1000);
+ final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+ final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+ NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
+ NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(cellNai, null);
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(wifiNai, cellNai);
+
String want = String.join("\n",
"dropped_events: 0",
"events <",
@@ -264,30 +417,6 @@ public class IpConnectivityMetricsTest {
" network_id: 0",
" time_ms: 300",
" transports: 0",
- " default_network_event <",
- " default_network_duration_ms: 0",
- " final_score: 0",
- " initial_score: 0",
- " ip_support: 0",
- " network_id <",
- " network_id: 102",
- " >",
- " no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 101",
- " >",
- " previous_network_ip_support: 1",
- " transport_types: 1",
- " transport_types: 2",
- " transport_types: 3",
- " >",
- ">",
- "events <",
- " if_name: \"\"",
- " link_layer: 4",
- " network_id: 0",
- " time_ms: 400",
- " transports: 0",
" ip_provisioning_event <",
" event_type: 1",
" if_name: \"\"",
@@ -298,7 +427,7 @@ public class IpConnectivityMetricsTest {
" if_name: \"\"",
" link_layer: 4",
" network_id: 0",
- " time_ms: 500",
+ " time_ms: 400",
" transports: 0",
" validation_probe_event <",
" latency_ms: 40730",
@@ -310,7 +439,7 @@ public class IpConnectivityMetricsTest {
" if_name: \"\"",
" link_layer: 4",
" network_id: 0",
- " time_ms: 600",
+ " time_ms: 500",
" transports: 0",
" apf_statistics <",
" dropped_ras: 2",
@@ -331,7 +460,7 @@ public class IpConnectivityMetricsTest {
" if_name: \"\"",
" link_layer: 4",
" network_id: 0",
- " time_ms: 700",
+ " time_ms: 600",
" transports: 0",
" ra_event <",
" dnssl_lifetime: -1",
@@ -344,6 +473,50 @@ public class IpConnectivityMetricsTest {
">",
"events <",
" if_name: \"\"",
+ " link_layer: 0",
+ " network_id: 100",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " network_id <",
+ " network_id: 100",
+ " >",
+ " no_default_network_duration_ms: 0",
+ " previous_network_id <",
+ " network_id: 0",
+ " >",
+ " previous_network_ip_support: 0",
+ " transport_types: 0",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 0",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " network_id <",
+ " network_id: 101",
+ " >",
+ " no_default_network_duration_ms: 0",
+ " previous_network_id <",
+ " network_id: 100",
+ " >",
+ " previous_network_ip_support: 2",
+ " transport_types: 1",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
" link_layer: 4",
" network_id: 100",
" time_ms: 0",
@@ -471,6 +644,26 @@ public class IpConnectivityMetricsTest {
mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
}
+ NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
+ NetworkAgentInfo nai = mock(NetworkAgentInfo.class);
+ when(nai.network()).thenReturn(new Network(netId));
+ when(nai.getCurrentScore()).thenReturn(score);
+ nai.linkProperties = new LinkProperties();
+ nai.networkCapabilities = new NetworkCapabilities();
+ for (int t : BitUtils.unpackBits(transports)) {
+ nai.networkCapabilities.addTransportType(t);
+ }
+ if (ipv4) {
+ nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24"));
+ nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0")));
+ }
+ if (ipv6) {
+ nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64"));
+ nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0")));
+ }
+ return nai;
+ }
+
List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
ArgumentCaptor<ConnectivityMetricsEvent> captor =
ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java b/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
index b5d333b8b230..f58ea7e9375f 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
@@ -48,8 +48,6 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SimChangeListenerTest {
- private static final int EVENT_UNM_UPDATE = 1;
-
@Mock private Context mContext;
private BroadcastInterceptingContext mServiceContext;
private Handler mHandler;
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
new file mode 100644
index 000000000000..6dd6059dcc4f
--- /dev/null
+++ b/tools/locked_region_code_injection/Android.bp
@@ -0,0 +1,12 @@
+java_library_host {
+ name: "lockedregioncodeinjection",
+ manifest: "manifest.txt",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "asm-6.0",
+ "asm-commons-6.0",
+ "asm-tree-6.0",
+ "asm-analysis-6.0",
+ "guava-21.0",
+ ],
+}
diff --git a/tools/locked_region_code_injection/Android.mk b/tools/locked_region_code_injection/Android.mk
deleted file mode 100644
index 3f6515154d42..000000000000
--- a/tools/locked_region_code_injection/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_JAR_MANIFEST := manifest.txt
-LOCAL_MODULE := lockedregioncodeinjection
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
- asm-6.0 \
- asm-commons-6.0 \
- asm-tree-6.0 \
- asm-analysis-6.0 \
- guava-21.0 \
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
index a60f2a2019d2..ee0e36c9f76e 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
@@ -76,7 +76,7 @@ class LockFindingClassVisitor extends ClassVisitor {
private MethodVisitor chain;
public LockFindingMethodVisitor(String owner, MethodNode mn, MethodVisitor chain) {
- super(Opcodes.ASM6, mn);
+ super(Utils.ASM_VERSION, mn);
assert owner != null;
this.owner = owner;
this.chain = chain;
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
index d2a2e7b90f2b..219c2b3a2fec 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Utils.java
@@ -19,7 +19,7 @@ import org.objectweb.asm.Opcodes;
public class Utils {
- public static final int ASM_VERSION = Opcodes.ASM5;
+ public static final int ASM_VERSION = Opcodes.ASM6;
/**
* Reads a comma separated configuration similar to the Jack definition.
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index a3a1054f869e..551e4df1c816 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -62,6 +62,8 @@ interface IWifiManager
WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult);
+ List<WifiConfiguration> getAllMatchingWifiConfigs(in ScanResult scanResult);
+
List<OsuProvider> getMatchingOsuProviders(in ScanResult scanResult);
int addOrUpdateNetwork(in WifiConfiguration config);
@@ -126,8 +128,6 @@ interface IWifiManager
void releaseMulticastLock();
- void setWifiApEnabled(in WifiConfiguration wifiConfig, boolean enable);
-
void updateInterfaceIpState(String ifaceName, int mode);
boolean startSoftAp(in WifiConfiguration wifiConfig);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b08b4b7c71b6..c2959d5e7ab7 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -68,6 +68,7 @@ import java.util.concurrent.CountDownLatch;
* leaks within the calling process.
* <p>
* It deals with several categories of items:
+ * </p>
* <ul>
* <li>The list of configured networks. The list can be viewed and updated, and
* attributes of individual entries can be modified.</li>
@@ -79,9 +80,11 @@ import java.util.concurrent.CountDownLatch;
* <li>It defines the names of various Intent actions that are broadcast upon
* any sort of change in Wi-Fi state.
* </ul>
+ * <p>
* This is the API to use when performing Wi-Fi specific operations. To perform
* operations that pertain to network connectivity at an abstract level, use
* {@link android.net.ConnectivityManager}.
+ * </p>
*/
@SystemService(Context.WIFI_SERVICE)
public class WifiManager {
@@ -1029,6 +1032,26 @@ public class WifiManager {
}
/**
+ * Return all matching WifiConfigurations for this ScanResult.
+ *
+ * An empty list will be returned when no configurations are installed or if no configurations
+ * match the ScanResult.
+ *
+ * @param scanResult scanResult that represents the BSSID
+ * @return A list of {@link WifiConfiguration}
+ * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
+ * @hide
+ */
+ public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
+ try {
+ return mService.getAllMatchingWifiConfigs(scanResult);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
* Returns a list of Hotspot 2.0 OSU (Online Sign-Up) providers associated with the given AP.
*
* An empty list will be returned if no match is found.
@@ -1555,7 +1578,21 @@ public class WifiManager {
* Request a scan for access points. Returns immediately. The availability
* of the results is made known later by means of an asynchronous event sent
* on completion of the scan.
- * @return {@code true} if the operation succeeded, i.e., the scan was initiated
+ * <p>
+ * To initiate a Wi-Fi scan, declare the
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}
+ * permission in the manifest, and perform these steps:
+ * </p>
+ * <ol style="1">
+ * <li>Invoke the following method:
+ * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).startScan()}</li>
+ * <li>
+ * Register a BroadcastReceiver to listen to
+ * {@code SCAN_RESULTS_AVAILABLE_ACTION}.</li>
+ * <li>When a broadcast is received, call:
+ * {@code ((WifiManager) getSystemService(WIFI_SERVICE)).getScanResults()}</li>
+ * </ol>
+ * @return {@code true} if the operation succeeded, i.e., the scan was initiated.
*/
public boolean startScan() {
return startScan(null);
@@ -1857,32 +1894,6 @@ public class WifiManager {
}
/**
- * This call is deprecated and removed. It is no longer used to
- * start WiFi Tethering. Please use {@link ConnectivityManager#startTethering(int, boolean,
- * ConnectivityManager#OnStartTetheringCallback)} if
- * the caller has proper permissions. Callers can also use the LocalOnlyHotspot feature for a
- * hotspot capable of communicating with co-located devices {@link
- * WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback)}.
- *
- * @param wifiConfig SSID, security and channel details as
- * part of WifiConfiguration
- * @return {@code false}
- *
- * @hide
- * @deprecated This API is nolonger supported.
- * @removed
- */
- @SystemApi
- @Deprecated
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
- public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
- String packageName = mContext.getOpPackageName();
-
- Log.w(TAG, packageName + " attempted call to setWifiApEnabled: enabled = " + enabled);
- return false;
- }
-
- /**
* Call allowing ConnectivityService to update WifiService with interface mode changes.
*
* The possible modes include: {@link IFACE_IP_MODE_TETHERED},
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index b235ccc7a89e..ee6f12b775a8 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -777,14 +777,4 @@ public class WifiManagerTest {
mWifiManager.unregisterLocalOnlyHotspotObserver();
verify(mWifiService).stopWatchLocalOnlyHotspot();
}
-
- /**
- * Verify that calls to setWifiApEnabled return false.
- */
- @Test
- public void testSetWifiApEnabledReturnsFalse() throws Exception {
- assertFalse(mWifiManager.setWifiApEnabled(null, true));
- assertFalse(mWifiManager.setWifiApEnabled(null, false));
- verify(mWifiService, never()).setWifiApEnabled(any(), anyBoolean());
- }
}