summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp24
-rw-r--r--Android.mk8
-rw-r--r--api/current.txt53
-rw-r--r--api/system-current.txt56
-rw-r--r--api/test-current.txt121
-rw-r--r--cmds/am/proto/instrumentation_data.proto1
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java9
-rw-r--r--cmds/am/src/com/android/commands/am/Instrument.java135
-rw-r--r--cmds/incident_helper/Android.bp31
-rw-r--r--cmds/incident_helper/IncidentHelper.cpp327
-rw-r--r--cmds/incident_helper/src/TextParserBase.cpp56
-rw-r--r--cmds/incident_helper/src/TextParserBase.h (renamed from cmds/incident_helper/IncidentHelper.h)39
-rw-r--r--cmds/incident_helper/src/ih_util.cpp (renamed from cmds/incident_helper/ih_util.cpp)63
-rw-r--r--cmds/incident_helper/src/ih_util.h (renamed from cmds/incident_helper/ih_util.h)28
-rw-r--r--cmds/incident_helper/src/main.cpp (renamed from cmds/incident_helper/main.cpp)4
-rw-r--r--cmds/incident_helper/src/parsers/KernelWakesParser.cpp79
-rw-r--r--cmds/incident_helper/src/parsers/KernelWakesParser.h33
-rw-r--r--cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp127
-rw-r--r--cmds/incident_helper/src/parsers/PageTypeInfoParser.h35
-rw-r--r--cmds/incident_helper/src/parsers/ProcrankParser.cpp112
-rw-r--r--cmds/incident_helper/src/parsers/ProcrankParser.h35
-rw-r--r--cmds/incident_helper/testdata/kernel_wakeups_short.txt3
-rw-r--r--cmds/incident_helper/tests/KernelWakesParser_test.cpp119
-rw-r--r--cmds/incident_helper/tests/PageTypeInfoParser_test.cpp113
-rw-r--r--cmds/incident_helper/tests/ProcrankParser_test.cpp (renamed from cmds/incident_helper/tests/IncidentHelper_test.cpp)116
-rw-r--r--cmds/incidentd/src/PrivacyBuffer.cpp4
-rw-r--r--cmds/incidentd/src/section_list.h2
-rw-r--r--cmds/incidentd/tests/PrivacyBuffer_test.cpp10
-rw-r--r--cmds/incidentd/tests/section_list.cpp6
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java3
-rw-r--r--cmds/statsd/.clang-format3
-rw-r--r--cmds/statsd/Android.mk10
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp67
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h26
-rw-r--r--cmds/statsd/src/StatsService.cpp16
-rw-r--r--cmds/statsd/src/StatsService.h3
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.cpp1
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.h2
-rw-r--r--cmds/statsd/src/condition/ConditionTracker.h4
-rw-r--r--cmds/statsd/src/condition/ConditionWizard.h10
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp32
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.h11
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp71
-rw-r--r--cmds/statsd/src/external/KernelWakelockPuller.cpp8
-rw-r--r--cmds/statsd/src/external/KernelWakelockPuller.h9
-rw-r--r--cmds/statsd/src/external/PullDataReceiver.h36
-rw-r--r--cmds/statsd/src/external/StatsPuller.h7
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp152
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.h73
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp3
-rw-r--r--cmds/statsd/src/logd/LogEvent.h2
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp16
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h11
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp270
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h57
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp11
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h10
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp6
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h10
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp18
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h2
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp246
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h92
-rw-r--r--cmds/statsd/src/metrics/duration_helper/DurationTracker.h89
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp227
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h55
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp195
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h57
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp104
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h2
-rw-r--r--cmds/statsd/src/packages/PackageInfoListener.h3
-rw-r--r--cmds/statsd/src/packages/UidMap.cpp105
-rw-r--r--cmds/statsd/src/packages/UidMap.h49
-rw-r--r--cmds/statsd/src/stats_events.proto183
-rw-r--r--cmds/statsd/src/stats_events_copy.proto355
-rw-r--r--cmds/statsd/src/stats_log.proto68
-rw-r--r--cmds/statsd/src/stats_util.h2
-rw-r--r--cmds/statsd/src/statsd_config.proto61
-rw-r--r--cmds/statsd/tests/MaxDurationTracker_test.cpp115
-rw-r--r--cmds/statsd/tests/MetricsManager_test.cpp49
-rw-r--r--cmds/statsd/tests/OringDurationTracker_test.cpp99
-rw-r--r--cmds/statsd/tests/UidMap_test.cpp57
-rw-r--r--core/java/android/app/Activity.java64
-rw-r--r--core/java/android/app/ActivityManager.java40
-rw-r--r--core/java/android/app/ActivityManagerInternal.java21
-rw-r--r--core/java/android/app/ActivityOptions.java22
-rw-r--r--core/java/android/app/AppOpsManager.java12
-rw-r--r--core/java/android/app/IActivityManager.aidl23
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/VrManager.java16
-rw-r--r--core/java/android/app/job/JobInfo.java67
-rw-r--r--core/java/android/app/job/JobWorkItem.java35
-rw-r--r--core/java/android/app/slice/SliceProvider.java12
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java3
-rw-r--r--core/java/android/bluetooth/BluetoothGattCharacteristic.java2
-rw-r--r--core/java/android/content/Intent.java13
-rw-r--r--core/java/android/content/pm/ActivityInfo.java11
-rw-r--r--core/java/android/content/pm/PackageParser.java25
-rw-r--r--core/java/android/content/res/AssetManager.java33
-rw-r--r--core/java/android/content/res/FontResourcesParser.java12
-rw-r--r--core/java/android/content/res/ResourcesImpl.java66
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java30
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java11
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java10
-rw-r--r--core/java/android/database/sqlite/SQLiteGlobal.java11
-rw-r--r--core/java/android/hardware/usb/UsbManager.java4
-rw-r--r--core/java/android/net/Network.java9
-rw-r--r--core/java/android/net/OWNERS2
-rw-r--r--core/java/android/os/BatteryStatsInternal.java35
-rw-r--r--core/java/android/os/Debug.java43
-rw-r--r--core/java/android/os/Environment.java5
-rw-r--r--core/java/android/os/HidlSupport.java23
-rw-r--r--core/java/android/os/HwBinder.java20
-rw-r--r--core/java/android/os/HwBlob.java20
-rw-r--r--core/java/android/os/HwRemoteBinder.java5
-rw-r--r--core/java/android/os/IStatsCompanionService.aidl4
-rw-r--r--core/java/android/os/LocaleList.java6
-rw-r--r--core/java/android/os/ShellCallback.java23
-rw-r--r--core/java/android/os/ShellCommand.java6
-rw-r--r--core/java/android/os/StrictMode.java38
-rw-r--r--core/java/android/os/TokenWatcher.java30
-rwxr-xr-xcore/java/android/provider/Settings.java5
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java19
-rw-r--r--core/java/android/service/vr/IVrManager.aidl8
-rw-r--r--core/java/android/util/proto/ProtoOutputStream.java82
-rw-r--r--core/java/android/view/IWindowManager.aidl6
-rw-r--r--core/java/android/view/NotificationHeaderView.java14
-rw-r--r--core/java/android/view/ViewConfiguration.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java91
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java7
-rw-r--r--core/java/android/view/autofill/AutofillManager.java29
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl2
-rw-r--r--core/java/android/webkit/ServiceWorkerClient.java6
-rw-r--r--core/java/android/webkit/WebView.java20
-rw-r--r--core/java/android/webkit/WebViewClient.java57
-rw-r--r--core/java/android/webkit/WebViewLibraryLoader.java4
-rw-r--r--core/java/android/webkit/WebViewProvider.java2
-rw-r--r--core/java/android/widget/AdapterView.java10
-rw-r--r--core/java/com/android/internal/app/IAppOpsCallback.aidl2
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl2
-rw-r--r--core/java/com/android/internal/app/IAssistDataReceiver.aidl (renamed from core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl)8
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl2
-rw-r--r--core/java/com/android/internal/app/LocaleHelper.java11
-rw-r--r--core/java/com/android/internal/app/LocalePicker.java15
-rw-r--r--core/java/com/android/internal/app/LocaleStore.java32
-rw-r--r--core/java/com/android/internal/app/NightDisplayController.java17
-rw-r--r--core/java/com/android/internal/net/OWNERS2
-rw-r--r--core/java/com/android/internal/os/BaseCommand.java8
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java12
-rw-r--r--core/java/com/android/internal/os/IShellCallback.aidl2
-rw-r--r--core/java/com/android/internal/policy/DividerSnapAlgorithm.java23
-rw-r--r--core/java/com/android/internal/util/StateMachine.java2
-rw-r--r--core/java/com/android/internal/widget/NotificationActionListLayout.java30
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/android_os_HwBinder.cpp22
-rw-r--r--core/jni/android_os_HwBlob.cpp162
-rw-r--r--core/jni/android_os_HwBlob.h2
-rw-r--r--core/jni/android_os_HwRemoteBinder.cpp49
-rw-r--r--core/jni/android_os_VintfObject.cpp5
-rw-r--r--core/jni/android_server_Watchdog.cpp112
-rw-r--r--core/jni/android_util_AssetManager.cpp31
-rw-r--r--core/proto/android/os/incident.proto6
-rw-r--r--core/proto/android/os/kernelwake.proto6
-rw-r--r--core/proto/android/os/pagetypeinfo.proto3
-rw-r--r--core/proto/android/os/procrank.proto4
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/proto/android/server/windowmanagertrace.proto56
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/drawable/dialog_background_material.xml2
-rw-r--r--core/res/res/layout/notification_template_header.xml1
-rw-r--r--core/res/res/layout/notification_template_material_ambient.xml23
-rw-r--r--core/res/res/values-bs/strings.xml13
-rw-r--r--core/res/res/values-in/strings.xml4
-rw-r--r--core/res/res/values-ko/strings.xml15
-rw-r--r--core/res/res/values-pl/strings.xml13
-rw-r--r--core/res/res/values-pt-rPT/strings.xml2
-rw-r--r--core/res/res/values-ta/strings.xml13
-rw-r--r--core/res/res/values/attrs.xml19
-rw-r--r--core/res/res/values/config.xml11
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/locale_config.xml3
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/styles_material.xml5
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/res/res/values/themes_device_defaults.xml172
-rw-r--r--core/res/res/values/themes_material.xml5
-rw-r--r--core/tests/coretests/res/font/samplexmlfont.xml14
-rw-r--r--core/tests/coretests/src/android/content/res/FontResourcesParserTest.java4
-rw-r--r--core/tests/coretests/src/android/widget/TextViewActivityTest.java22
-rw-r--r--core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java4
-rw-r--r--core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java33
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java95
-rw-r--r--data/etc/OWNERS7
-rw-r--r--graphics/java/android/graphics/Typeface.java4
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java84
-rw-r--r--libs/androidfw/AssetManager.cpp87
-rw-r--r--libs/androidfw/ResourceTypes.cpp128
-rw-r--r--libs/androidfw/ZipFileRO.cpp24
-rw-r--r--libs/androidfw/include/androidfw/AssetManager.h27
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h11
-rw-r--r--libs/androidfw/include/androidfw/ZipFileRO.h6
-rw-r--r--libs/androidfw/tests/ConfigLocale_test.cpp42
-rw-r--r--libs/hwui/Extensions.cpp6
-rw-r--r--libs/hwui/Extensions.h4
-rw-r--r--libs/hwui/OpenGLReadback.cpp3
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp75
-rwxr-xr-xlibs/hwui/tests/scripts/prep_taieye.sh50
-rw-r--r--libs/protoutil/Android.bp39
-rw-r--r--libs/protoutil/Android.mk39
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h64
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp191
-rw-r--r--location/java/android/location/Location.java4
-rw-r--r--media/java/android/media/ExifInterface.java83
-rw-r--r--media/java/android/media/MediaFormat.java90
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java162
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp117
-rw-r--r--media/jni/android_mtp_MtpDatabase.cpp1
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_laptop.xml26
-rw-r--r--packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml25
-rw-r--r--packages/SettingsLib/res/drawable/ic_settings_print.xml28
-rw-r--r--packages/SettingsLib/res/layout/preference_two_target.xml4
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml2
-rw-r--r--packages/SettingsLib/res/values/dimens.xml1
-rw-r--r--packages/SettingsLib/res/values/strings.xml21
-rw-r--r--packages/SettingsLib/res/values/styles.xml5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java25
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java82
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java11
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java24
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java65
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java34
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/UtilsTest.java54
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java16
-rw-r--r--packages/SystemUI/res-keyguard/values-bs/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ko/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-pl/strings.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ta/strings.xml3
-rw-r--r--packages/SystemUI/res/layout/operator_name.xml31
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label.xml3
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml5
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml2
-rw-r--r--packages/SystemUI/res/values-in/strings.xml10
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java84
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java117
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java50
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java45
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java (renamed from packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListener.java)25
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java (renamed from packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListeners.java)2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/DemoMode.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/RecentsComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/DockState.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java175
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java458
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java184
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java15
-rw-r--r--proto/src/metrics_constants.proto39
-rw-r--r--proto/src/wifi.proto7
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java1
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java18
-rw-r--r--services/autofill/java/com/android/server/autofill/Helper.java9
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java20
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java5
-rw-r--r--services/core/java/com/android/server/BatteryService.java22
-rw-r--r--services/core/java/com/android/server/Watchdog.java20
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java398
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java12
-rw-r--r--services/core/java/com/android/server/am/AssistDataReceiverProxy.java87
-rw-r--r--services/core/java/com/android/server/am/AssistDataRequester.java307
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java14
-rw-r--r--services/core/java/com/android/server/am/LockTaskController.java225
-rw-r--r--services/core/java/com/android/server/am/RecentTasks.java14
-rw-r--r--services/core/java/com/android/server/am/RunningTasks.java6
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java2
-rw-r--r--services/core/java/com/android/server/am/UserController.java8
-rw-r--r--services/core/java/com/android/server/content/ContentService.java4
-rw-r--r--services/core/java/com/android/server/job/JobServiceContext.java2
-rw-r--r--services/core/java/com/android/server/job/controllers/ConnectivityController.java57
-rw-r--r--services/core/java/com/android/server/job/controllers/JobStatus.java43
-rw-r--r--services/core/java/com/android/server/location/GeofenceManager.java3
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java25
-rw-r--r--services/core/java/com/android/server/net/OWNERS2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java39
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java78
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java1
-rw-r--r--services/core/java/com/android/server/pm/permission/OWNERS7
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java256
-rw-r--r--services/core/java/com/android/server/vr/VrManagerService.java26
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java100
-rw-r--r--services/core/java/com/android/server/wm/DimLayer.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java18
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java20
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java179
-rw-r--r--services/core/java/com/android/server/wm/DragInputEventReceiver.java151
-rw-r--r--services/core/java/com/android/server/wm/DragState.java117
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java53
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java24
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java8
-rw-r--r--services/core/java/com/android/server/wm/Session.java8
-rw-r--r--services/core/java/com/android/server/wm/Task.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java185
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java57
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java183
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java197
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp2
-rw-r--r--services/core/jni/onload.cpp1
-rw-r--r--services/coverage/java/com/android/server/coverage/CoverageService.java2
-rw-r--r--services/java/com/android/server/SystemServer.java624
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java149
-rw-r--r--services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java316
-rw-r--r--services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java82
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java194
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java8
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java295
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java50
-rw-r--r--telephony/java/android/telephony/MbmsDownloadSession.java9
-rw-r--r--telephony/java/android/telephony/MbmsStreamingSession.java9
-rw-r--r--telephony/java/android/telephony/SignalStrength.java61
-rw-r--r--telephony/java/android/telephony/SmsManager.java245
-rw-r--r--telephony/java/android/telephony/SmsMessage.java26
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java32
-rw-r--r--telephony/java/android/telephony/mbms/FileServiceInfo.java2
-rw-r--r--telephony/java/android/telephony/mbms/MbmsUtils.java56
-rw-r--r--telephony/java/android/telephony/mbms/StreamingServiceInfo.java2
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java2
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java2
-rw-r--r--telephony/java/com/android/internal/telephony/ISms.aidl101
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/SmsMessage.java60
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java113
-rw-r--r--telephony/java/com/android/internal/telephony/uicc/IccUtils.java8
-rw-r--r--tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java41
-rw-r--r--tests/testables/tests/AndroidManifest.xml4
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser.cpp4
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser_test.cpp43
-rw-r--r--tools/incident_section_gen/main.cpp278
-rw-r--r--tools/stats_log_api_gen/Collation.cpp23
-rw-r--r--tools/stats_log_api_gen/Collation.h10
-rw-r--r--tools/stats_log_api_gen/main.cpp32
-rw-r--r--tools/stats_log_api_gen/test_collation.cpp35
-rw-r--r--tools/streaming_proto/Android.bp25
-rw-r--r--tools/streaming_proto/cpp/main.cpp140
-rw-r--r--tools/streaming_proto/java/main.cpp174
-rw-r--r--tools/streaming_proto/stream.proto32
-rw-r--r--tools/streaming_proto/stream_proto_utils.cpp102
-rw-r--r--tools/streaming_proto/stream_proto_utils.h29
-rw-r--r--tools/streaming_proto/string_utils.h3
-rw-r--r--wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl1
-rw-r--r--wifi/java/android/net/wifi/rtt/RangingResultCallback.java10
-rw-r--r--wifi/java/android/net/wifi/rtt/WifiRttManager.java36
396 files changed, 12794 insertions, 4672 deletions
diff --git a/Android.bp b/Android.bp
index c89cc400171d..5c1ccb7cc639 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,6 +47,7 @@ cc_library {
srcs: [
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
+ "tools/streaming_proto/stream.proto",
],
},
android: {
@@ -61,7 +62,7 @@ cc_library {
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
"core/proto/android/service/graphicsstats.proto",
- "libs/incident/proto/android/privacy.proto",
+ "tools/streaming_proto/stream.proto",
],
shared: {
enabled: false,
@@ -70,6 +71,27 @@ cc_library {
},
}
+gensrcs {
+ name: "gen-platform-proto-constants",
+ depfile: true,
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-cppstream",
+ ],
+
+ srcs: [
+ "core/proto/android/os/kernelwake.proto",
+ "core/proto/android/os/pagetypeinfo.proto",
+ "core/proto/android/os/procrank.proto",
+ ],
+
+ // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
+ cmd: "PATH=$$PATH:$$(dirname $(location protoc-gen-cppstream)) $(location aprotoc) --plugin=protoc-gen-cpp-stream=$(location protoc-gen-cppstream) --dependency_out=$(depfile) --cppstream_out=$(genDir)/ -Iexternal/protobuf/src -I . $(in)",
+
+ output_extension = "proto.h",
+}
+
subdirs = [
"cmds/*",
"core/*",
diff --git a/Android.mk b/Android.mk
index 9890bb401404..1ed8a2506600 100644
--- a/Android.mk
+++ b/Android.mk
@@ -380,7 +380,7 @@ LOCAL_SRC_FILES += \
core/java/android/speech/tts/ITextToSpeechService.aidl \
core/java/com/android/internal/app/IAppOpsCallback.aidl \
core/java/com/android/internal/app/IAppOpsService.aidl \
- core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl \
+ core/java/com/android/internal/app/IAssistDataReceiver.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/ISoundTriggerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
@@ -989,7 +989,8 @@ framework_docs_LOCAL_DROIDDOC_SOURCE_PATH := \
framework_docs_LOCAL_INTERMEDIATE_SOURCES := \
$(framework_res_source_path)/android/R.java \
$(framework_res_source_path)/android/Manifest.java \
- $(framework_res_source_path)/com/android/internal/R.java
+ $(framework_res_source_path)/com/android/internal/R.java \
+ $(patsubst $(TARGET_OUT_COMMON_INTERMEDIATES)/%,%,$(libcore_to_document_generated))
framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
core-oj \
@@ -1060,7 +1061,7 @@ framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
frameworks/base/docs/knowntags.txt \
- libcore/Docs.mk
+ $(libcore_to_document_generated)
samples_dir := development/samples/browseable
@@ -1552,6 +1553,7 @@ LOCAL_PROTOC_FLAGS := \
-Iexternal/protobuf/src
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
+ tools/streaming_proto/stream.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/api/current.txt b/api/current.txt
index bdfa68ef6615..7074de5790f5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -484,6 +484,7 @@ package android {
field public static final int detailSocialSummary = 16843428; // 0x10102a4
field public static final int detailsElementBackground = 16843598; // 0x101034e
field public static final int dial = 16843010; // 0x1010102
+ field public static final int dialogCornerRadius = 16844145; // 0x1010571
field public static final int dialogIcon = 16843252; // 0x10101f4
field public static final int dialogLayout = 16843255; // 0x10101f7
field public static final int dialogMessage = 16843251; // 0x10101f3
@@ -612,6 +613,7 @@ package android {
field public static final int fontProviderPackage = 16844119; // 0x1010557
field public static final int fontProviderQuery = 16844113; // 0x1010551
field public static final int fontStyle = 16844095; // 0x101053f
+ field public static final int fontVariationSettings = 16844144; // 0x1010570
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
@@ -6834,6 +6836,7 @@ package android.app.job {
method public int getBackoffPolicy();
method public android.content.ClipData getClipData();
method public int getClipGrantFlags();
+ method public long getEstimatedNetworkBytes();
method public android.os.PersistableBundle getExtras();
method public long getFlexMillis();
method public int getId();
@@ -6861,6 +6864,7 @@ package android.app.job {
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+ field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_METERED = 4; // 0x4
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
@@ -6874,6 +6878,7 @@ package android.app.job {
method public android.app.job.JobInfo build();
method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int);
+ method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
@@ -6948,8 +6953,10 @@ package android.app.job {
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
+ ctor public JobWorkItem(android.content.Intent, long);
method public int describeContents();
method public int getDeliveryCount();
+ method public long getEstimatedNetworkBytes();
method public android.content.Intent getIntent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
@@ -12010,6 +12017,7 @@ package android.database.sqlite {
method public java.lang.String[] getColumnNames();
method public int getCount();
method public android.database.sqlite.SQLiteDatabase getDatabase();
+ method public void setFillWindowForwardOnly(boolean);
method public void setSelectionArguments(java.lang.String[]);
}
@@ -18264,13 +18272,11 @@ package android.icu.text {
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18278,12 +18284,8 @@ package android.icu.text {
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18294,19 +18296,13 @@ package android.icu.text {
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18315,15 +18311,10 @@ package android.icu.text {
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18331,15 +18322,12 @@ package android.icu.text {
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -22810,6 +22798,10 @@ package android.media {
field public static final java.lang.String KEY_DURATION = "durationUs";
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
+ field public static final java.lang.String KEY_GRID_COLS = "grid-cols";
+ field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height";
+ field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
+ field public static final java.lang.String KEY_GRID_WIDTH = "grid-width";
field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -22853,6 +22845,7 @@ package android.media {
field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw";
field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+ field public static final java.lang.String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
@@ -22945,9 +22938,13 @@ package android.media {
ctor public MediaMetadataRetriever();
method public java.lang.String extractMetadata(int);
method public byte[] getEmbeddedPicture();
+ method public android.graphics.Bitmap getFrameAtIndex(int);
method public android.graphics.Bitmap getFrameAtTime(long, int);
method public android.graphics.Bitmap getFrameAtTime(long);
method public android.graphics.Bitmap getFrameAtTime();
+ method public android.graphics.Bitmap[] getFramesAtIndex(int, int);
+ method public android.graphics.Bitmap getImageAtIndex(int);
+ method public android.graphics.Bitmap getPrimaryImage();
method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
method public void release();
method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
@@ -22970,11 +22967,18 @@ package android.media {
field public static final int METADATA_KEY_DURATION = 9; // 0x9
field public static final int METADATA_KEY_GENRE = 6; // 0x6
field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10
+ field public static final int METADATA_KEY_HAS_IMAGE = 26; // 0x1a
field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11
+ field public static final int METADATA_KEY_IMAGE_COUNT = 27; // 0x1b
+ field public static final int METADATA_KEY_IMAGE_HEIGHT = 30; // 0x1e
+ field public static final int METADATA_KEY_IMAGE_PRIMARY = 28; // 0x1c
+ field public static final int METADATA_KEY_IMAGE_ROTATION = 31; // 0x1f
+ field public static final int METADATA_KEY_IMAGE_WIDTH = 29; // 0x1d
field public static final int METADATA_KEY_LOCATION = 23; // 0x17
field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc
field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa
field public static final int METADATA_KEY_TITLE = 7; // 0x7
+ field public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; // 0x20
field public static final int METADATA_KEY_VIDEO_HEIGHT = 19; // 0x13
field public static final int METADATA_KEY_VIDEO_ROTATION = 24; // 0x18
field public static final int METADATA_KEY_VIDEO_WIDTH = 18; // 0x12
@@ -25812,7 +25816,6 @@ package android.net {
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
@@ -39901,12 +39904,14 @@ package android.telephony {
field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
+ field public static final java.lang.String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
+ field public static final java.lang.String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
@@ -39920,8 +39925,8 @@ package android.telephony {
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
- field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
+ field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -40655,6 +40660,10 @@ package android.telephony {
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
+ field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
+ field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
+ field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
+ field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff
field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
field public static final int DATA_ACTIVITY_IN = 1; // 0x1
field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index e3162e3f0881..af15a0042c1f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -88,6 +88,7 @@ package android {
field public static final java.lang.String CAPTURE_SECURE_VIDEO_OUTPUT = "android.permission.CAPTURE_SECURE_VIDEO_OUTPUT";
field public static final java.lang.String CAPTURE_TV_INPUT = "android.permission.CAPTURE_TV_INPUT";
field public static final java.lang.String CAPTURE_VIDEO_OUTPUT = "android.permission.CAPTURE_VIDEO_OUTPUT";
+ field public static final java.lang.String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
field public static final java.lang.String CHANGE_COMPONENT_ENABLED_STATE = "android.permission.CHANGE_COMPONENT_ENABLED_STATE";
field public static final java.lang.String CHANGE_CONFIGURATION = "android.permission.CHANGE_CONFIGURATION";
field public static final java.lang.String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST";
@@ -616,6 +617,7 @@ package android {
field public static final int detailSocialSummary = 16843428; // 0x10102a4
field public static final int detailsElementBackground = 16843598; // 0x101034e
field public static final int dial = 16843010; // 0x1010102
+ field public static final int dialogCornerRadius = 16844145; // 0x1010571
field public static final int dialogIcon = 16843252; // 0x10101f4
field public static final int dialogLayout = 16843255; // 0x10101f7
field public static final int dialogMessage = 16843251; // 0x10101f3
@@ -744,6 +746,7 @@ package android {
field public static final int fontProviderPackage = 16844119; // 0x1010557
field public static final int fontProviderQuery = 16844113; // 0x1010551
field public static final int fontStyle = 16844095; // 0x101053f
+ field public static final int fontVariationSettings = 16844144; // 0x1010570
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
@@ -7276,6 +7279,7 @@ package android.app.job {
method public int getBackoffPolicy();
method public android.content.ClipData getClipData();
method public int getClipGrantFlags();
+ method public long getEstimatedNetworkBytes();
method public android.os.PersistableBundle getExtras();
method public long getFlexMillis();
method public int getId();
@@ -7303,6 +7307,7 @@ package android.app.job {
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+ field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_METERED = 4; // 0x4
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
@@ -7316,6 +7321,7 @@ package android.app.job {
method public android.app.job.JobInfo build();
method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int);
+ method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
@@ -7391,8 +7397,10 @@ package android.app.job {
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
+ ctor public JobWorkItem(android.content.Intent, long);
method public int describeContents();
method public int getDeliveryCount();
+ method public long getEstimatedNetworkBytes();
method public android.content.Intent getIntent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
@@ -7670,6 +7678,7 @@ package android.app.usage {
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
+ method public void setAppStandbyBucket(java.lang.String, int);
method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
field public static final int INTERVAL_BEST = 4; // 0x4
field public static final int INTERVAL_DAILY = 0; // 0x0
@@ -10228,6 +10237,7 @@ package android.content {
field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
field public static final java.lang.String EXTRA_QUICK_VIEW_FEATURES = "android.intent.extra.QUICK_VIEW_FEATURES";
field public static final java.lang.String EXTRA_QUIET_MODE = "android.intent.extra.QUIET_MODE";
+ field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON";
field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
@@ -12754,6 +12764,7 @@ package android.database.sqlite {
method public java.lang.String[] getColumnNames();
method public int getCount();
method public android.database.sqlite.SQLiteDatabase getDatabase();
+ method public void setFillWindowForwardOnly(boolean);
method public void setSelectionArguments(java.lang.String[]);
}
@@ -19820,13 +19831,11 @@ package android.icu.text {
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -19834,12 +19843,8 @@ package android.icu.text {
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -19850,19 +19855,13 @@ package android.icu.text {
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -19871,15 +19870,10 @@ package android.icu.text {
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -19887,15 +19881,12 @@ package android.icu.text {
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -24700,6 +24691,10 @@ package android.media {
field public static final java.lang.String KEY_DURATION = "durationUs";
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
+ field public static final java.lang.String KEY_GRID_COLS = "grid-cols";
+ field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height";
+ field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
+ field public static final java.lang.String KEY_GRID_WIDTH = "grid-width";
field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -24743,6 +24738,7 @@ package android.media {
field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw";
field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+ field public static final java.lang.String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
@@ -24835,9 +24831,13 @@ package android.media {
ctor public MediaMetadataRetriever();
method public java.lang.String extractMetadata(int);
method public byte[] getEmbeddedPicture();
+ method public android.graphics.Bitmap getFrameAtIndex(int);
method public android.graphics.Bitmap getFrameAtTime(long, int);
method public android.graphics.Bitmap getFrameAtTime(long);
method public android.graphics.Bitmap getFrameAtTime();
+ method public android.graphics.Bitmap[] getFramesAtIndex(int, int);
+ method public android.graphics.Bitmap getImageAtIndex(int);
+ method public android.graphics.Bitmap getPrimaryImage();
method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
method public void release();
method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
@@ -24860,11 +24860,18 @@ package android.media {
field public static final int METADATA_KEY_DURATION = 9; // 0x9
field public static final int METADATA_KEY_GENRE = 6; // 0x6
field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10
+ field public static final int METADATA_KEY_HAS_IMAGE = 26; // 0x1a
field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11
+ field public static final int METADATA_KEY_IMAGE_COUNT = 27; // 0x1b
+ field public static final int METADATA_KEY_IMAGE_HEIGHT = 30; // 0x1e
+ field public static final int METADATA_KEY_IMAGE_PRIMARY = 28; // 0x1c
+ field public static final int METADATA_KEY_IMAGE_ROTATION = 31; // 0x1f
+ field public static final int METADATA_KEY_IMAGE_WIDTH = 29; // 0x1d
field public static final int METADATA_KEY_LOCATION = 23; // 0x17
field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc
field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa
field public static final int METADATA_KEY_TITLE = 7; // 0x7
+ field public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; // 0x20
field public static final int METADATA_KEY_VIDEO_HEIGHT = 19; // 0x13
field public static final int METADATA_KEY_VIDEO_ROTATION = 24; // 0x18
field public static final int METADATA_KEY_VIDEO_WIDTH = 18; // 0x12
@@ -28054,7 +28061,6 @@ package android.net {
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
@@ -43416,6 +43422,7 @@ package android.telephony {
field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
+ field public static final java.lang.String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
@@ -43423,6 +43430,7 @@ package android.telephony {
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
+ field public static final java.lang.String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
@@ -43436,8 +43444,8 @@ package android.telephony {
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
- field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
+ field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -44277,6 +44285,10 @@ package android.telephony {
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
+ field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
+ field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
+ field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff
field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
field public static final int DATA_ACTIVITY_IN = 1; // 0x1
field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 19c75221de59..0bc0cd9efd7f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -484,6 +484,7 @@ package android {
field public static final int detailSocialSummary = 16843428; // 0x10102a4
field public static final int detailsElementBackground = 16843598; // 0x101034e
field public static final int dial = 16843010; // 0x1010102
+ field public static final int dialogCornerRadius = 16844145; // 0x1010571
field public static final int dialogIcon = 16843252; // 0x10101f4
field public static final int dialogLayout = 16843255; // 0x10101f7
field public static final int dialogMessage = 16843251; // 0x10101f3
@@ -612,6 +613,7 @@ package android {
field public static final int fontProviderPackage = 16844119; // 0x1010557
field public static final int fontProviderQuery = 16844113; // 0x1010551
field public static final int fontStyle = 16844095; // 0x101053f
+ field public static final int fontVariationSettings = 16844144; // 0x1010570
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
@@ -3872,6 +3874,7 @@ package android.app {
method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
method public deprecated void restartPackage(java.lang.String);
method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
+ method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect) throws java.lang.SecurityException;
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
method public static boolean supportsMultiWindow(android.content.Context);
@@ -3885,6 +3888,8 @@ package android.app {
field public static final int MOVE_TASK_WITH_HOME = 1; // 0x1
field public static final int RECENT_IGNORE_UNAVAILABLE = 2; // 0x2
field public static final int RECENT_WITH_EXCLUDED = 1; // 0x1
+ field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
+ field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
}
public static class ActivityManager.AppTask {
@@ -6905,6 +6910,7 @@ package android.app.job {
method public int getBackoffPolicy();
method public android.content.ClipData getClipData();
method public int getClipGrantFlags();
+ method public long getEstimatedNetworkBytes();
method public android.os.PersistableBundle getExtras();
method public long getFlexMillis();
method public int getId();
@@ -6932,6 +6938,7 @@ package android.app.job {
field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+ field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
field public static final int NETWORK_TYPE_METERED = 4; // 0x4
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
@@ -6945,6 +6952,7 @@ package android.app.job {
method public android.app.job.JobInfo build();
method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int);
+ method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
@@ -7019,8 +7027,10 @@ package android.app.job {
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
+ ctor public JobWorkItem(android.content.Intent, long);
method public int describeContents();
method public int getDeliveryCount();
+ method public long getEstimatedNetworkBytes();
method public android.content.Intent getIntent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
@@ -12097,6 +12107,7 @@ package android.database.sqlite {
method public java.lang.String[] getColumnNames();
method public int getCount();
method public android.database.sqlite.SQLiteDatabase getDatabase();
+ method public void setFillWindowForwardOnly(boolean);
method public void setSelectionArguments(java.lang.String[]);
}
@@ -18406,13 +18417,11 @@ package android.icu.text {
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18420,12 +18429,8 @@ package android.icu.text {
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18436,19 +18441,13 @@ package android.icu.text {
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18457,15 +18456,10 @@ package android.icu.text {
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18473,15 +18467,12 @@ package android.icu.text {
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -23010,6 +23001,10 @@ package android.media {
field public static final java.lang.String KEY_DURATION = "durationUs";
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
+ field public static final java.lang.String KEY_GRID_COLS = "grid-cols";
+ field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height";
+ field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
+ field public static final java.lang.String KEY_GRID_WIDTH = "grid-width";
field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -23053,6 +23048,7 @@ package android.media {
field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw";
field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+ field public static final java.lang.String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
@@ -23145,9 +23141,13 @@ package android.media {
ctor public MediaMetadataRetriever();
method public java.lang.String extractMetadata(int);
method public byte[] getEmbeddedPicture();
+ method public android.graphics.Bitmap getFrameAtIndex(int);
method public android.graphics.Bitmap getFrameAtTime(long, int);
method public android.graphics.Bitmap getFrameAtTime(long);
method public android.graphics.Bitmap getFrameAtTime();
+ method public android.graphics.Bitmap[] getFramesAtIndex(int, int);
+ method public android.graphics.Bitmap getImageAtIndex(int);
+ method public android.graphics.Bitmap getPrimaryImage();
method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
method public void release();
method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
@@ -23170,11 +23170,18 @@ package android.media {
field public static final int METADATA_KEY_DURATION = 9; // 0x9
field public static final int METADATA_KEY_GENRE = 6; // 0x6
field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10
+ field public static final int METADATA_KEY_HAS_IMAGE = 26; // 0x1a
field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11
+ field public static final int METADATA_KEY_IMAGE_COUNT = 27; // 0x1b
+ field public static final int METADATA_KEY_IMAGE_HEIGHT = 30; // 0x1e
+ field public static final int METADATA_KEY_IMAGE_PRIMARY = 28; // 0x1c
+ field public static final int METADATA_KEY_IMAGE_ROTATION = 31; // 0x1f
+ field public static final int METADATA_KEY_IMAGE_WIDTH = 29; // 0x1d
field public static final int METADATA_KEY_LOCATION = 23; // 0x17
field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc
field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa
field public static final int METADATA_KEY_TITLE = 7; // 0x7
+ field public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; // 0x20
field public static final int METADATA_KEY_VIDEO_HEIGHT = 19; // 0x13
field public static final int METADATA_KEY_VIDEO_ROTATION = 24; // 0x18
field public static final int METADATA_KEY_VIDEO_WIDTH = 18; // 0x12
@@ -26012,7 +26019,6 @@ package android.net {
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
@@ -40295,12 +40301,14 @@ package android.telephony {
field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
+ field public static final java.lang.String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
+ field public static final java.lang.String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
@@ -40314,8 +40322,8 @@ package android.telephony {
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
- field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
+ field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -40610,6 +40618,7 @@ package android.telephony {
field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
+ field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
field public static final int RESULT_CANCELLED = 2; // 0x2
field public static final int RESULT_DOWNLOAD_FAILURE = 6; // 0x6
field public static final int RESULT_EXPIRED = 3; // 0x3
@@ -40631,6 +40640,7 @@ package android.telephony {
method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler);
method public void requestUpdateStreamingServices(java.util.List<java.lang.String>);
method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler);
+ field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
}
public class NeighboringCellInfo implements android.os.Parcelable {
@@ -41049,6 +41059,10 @@ package android.telephony {
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
+ field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1
+ field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2
+ field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0
+ field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff
field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
field public static final int DATA_ACTIVITY_IN = 1; // 0x1
field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
@@ -41312,6 +41326,7 @@ package android.telephony.mbms {
}
public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ ctor public FileServiceInfo(java.util.Map<java.util.Locale, java.lang.String>, java.lang.String, java.util.List<java.util.Locale>, java.lang.String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>);
method public int describeContents();
method public java.util.List<android.telephony.mbms.FileInfo> getFiles();
method public void writeToParcel(android.os.Parcel, int);
@@ -41410,6 +41425,7 @@ package android.telephony.mbms {
}
public final class StreamingServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ ctor public StreamingServiceInfo(java.util.Map<java.util.Locale, java.lang.String>, java.lang.String, java.util.List<java.util.Locale>, java.lang.String, java.util.Date, java.util.Date);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.mbms.StreamingServiceInfo> CREATOR;
@@ -41417,6 +41433,37 @@ package android.telephony.mbms {
}
+package android.telephony.mbms.vendor {
+
+ public class MbmsDownloadServiceBase extends android.os.Binder {
+ ctor public MbmsDownloadServiceBase();
+ method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public void dispose(int) throws android.os.RemoteException;
+ method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
+ method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
+ method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
+ method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ }
+
+ public class MbmsStreamingServiceBase extends android.os.Binder {
+ ctor public MbmsStreamingServiceBase();
+ method public void dispose(int) throws android.os.RemoteException;
+ method public android.net.Uri getPlaybackUri(int, java.lang.String) throws android.os.RemoteException;
+ method public int initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public int requestUpdateStreamingServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public int startStreaming(int, java.lang.String, android.telephony.mbms.StreamingServiceCallback) throws android.os.RemoteException;
+ method public void stopStreaming(int, java.lang.String) throws android.os.RemoteException;
+ }
+
+}
+
package android.test {
public abstract deprecated class ActivityInstrumentationTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase {
@@ -44934,25 +44981,25 @@ package android.util.proto {
field public static final long FIELD_COUNT_UNKNOWN = 0L; // 0x0L
field public static final int FIELD_ID_MASK = -8; // 0xfffffff8
field public static final int FIELD_ID_SHIFT = 3; // 0x3
- field public static final long FIELD_TYPE_BOOL = 55834574848L; // 0xd00000000L
- field public static final long FIELD_TYPE_BYTES = 64424509440L; // 0xf00000000L
+ field public static final long FIELD_TYPE_BOOL = 34359738368L; // 0x800000000L
+ field public static final long FIELD_TYPE_BYTES = 51539607552L; // 0xc00000000L
field public static final long FIELD_TYPE_DOUBLE = 4294967296L; // 0x100000000L
- field public static final long FIELD_TYPE_ENUM = 68719476736L; // 0x1000000000L
- field public static final long FIELD_TYPE_FIXED32 = 38654705664L; // 0x900000000L
- field public static final long FIELD_TYPE_FIXED64 = 42949672960L; // 0xa00000000L
+ field public static final long FIELD_TYPE_ENUM = 60129542144L; // 0xe00000000L
+ field public static final long FIELD_TYPE_FIXED32 = 30064771072L; // 0x700000000L
+ field public static final long FIELD_TYPE_FIXED64 = 25769803776L; // 0x600000000L
field public static final long FIELD_TYPE_FLOAT = 8589934592L; // 0x200000000L
- field public static final long FIELD_TYPE_INT32 = 12884901888L; // 0x300000000L
- field public static final long FIELD_TYPE_INT64 = 17179869184L; // 0x400000000L
+ field public static final long FIELD_TYPE_INT32 = 21474836480L; // 0x500000000L
+ field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L
field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L
- field public static final long FIELD_TYPE_OBJECT = 73014444032L; // 0x1100000000L
- field public static final long FIELD_TYPE_SFIXED32 = 47244640256L; // 0xb00000000L
- field public static final long FIELD_TYPE_SFIXED64 = 51539607552L; // 0xc00000000L
+ field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L
+ field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L
+ field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L
field public static final int FIELD_TYPE_SHIFT = 32; // 0x20
- field public static final long FIELD_TYPE_SINT32 = 30064771072L; // 0x700000000L
- field public static final long FIELD_TYPE_SINT64 = 34359738368L; // 0x800000000L
- field public static final long FIELD_TYPE_STRING = 60129542144L; // 0xe00000000L
- field public static final long FIELD_TYPE_UINT32 = 21474836480L; // 0x500000000L
- field public static final long FIELD_TYPE_UINT64 = 25769803776L; // 0x600000000L
+ field public static final long FIELD_TYPE_SINT32 = 73014444032L; // 0x1100000000L
+ field public static final long FIELD_TYPE_SINT64 = 77309411328L; // 0x1200000000L
+ field public static final long FIELD_TYPE_STRING = 38654705664L; // 0x900000000L
+ field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L
+ field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L
field public static final long FIELD_TYPE_UNKNOWN = 0L; // 0x0L
field public static final java.lang.String TAG = "ProtoOutputStream";
field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto
index 12a18a2a035f..8e29f9645568 100644
--- a/cmds/am/proto/instrumentation_data.proto
+++ b/cmds/am/proto/instrumentation_data.proto
@@ -28,6 +28,7 @@ message ResultsBundleEntry {
optional double value_double = 5;
optional sint64 value_long = 6;
optional ResultsBundle value_bundle = 7;
+ optional bytes value_bytes = 8;
}
message ResultsBundle {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index ab075ee0e9f2..813335a688ab 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -98,7 +98,8 @@ public class Am extends BaseCommand {
static final class MyShellCallback extends ShellCallback {
boolean mActive = true;
- @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ @Override public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext,
+ String mode) {
if (!mActive) {
System.err.println("Open attempt after active for: " + path);
return null;
@@ -159,7 +160,11 @@ public class Am extends BaseCommand {
} else if (opt.equals("-r")) {
instrument.rawMode = true;
} else if (opt.equals("-m")) {
- instrument.proto = true;
+ instrument.protoStd = true;
+ } else if (opt.equals("-f")) {
+ instrument.protoFile = true;
+ if (peekNextArg() != null && !peekNextArg().startsWith("-"))
+ instrument.logPath = nextArg();
} else if (opt.equals("-e")) {
final String argKey = nextArgRequired();
final String argValue = nextArgRequired();
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index b69ef1c2fca5..93b9f58d51d3 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -25,23 +25,32 @@ import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.proto.ProtoOutputStream;
import android.view.IWindowManager;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
/**
* Runs the am instrument command
*/
public class Instrument {
+ public static final String DEFAULT_LOG_DIR = "instrument-logs";
+
private final IActivityManager mAm;
private final IPackageManager mPm;
private final IWindowManager mWm;
@@ -50,7 +59,9 @@ public class Instrument {
public String profileFile = null;
public boolean wait = false;
public boolean rawMode = false;
- public boolean proto = false;
+ boolean protoStd = false; // write proto to stdout
+ boolean protoFile = false; // write proto to a file
+ String logPath = null;
public boolean noWindowAnimation = false;
public String abi = null;
public int userId = UserHandle.USER_CURRENT;
@@ -178,18 +189,49 @@ public class Instrument {
* Printer for the protobuf based status reporting.
*/
private class ProtoStatusReporter implements StatusReporter {
+
+ private File mLog;
+
+ ProtoStatusReporter() {
+ if (protoFile) {
+ if (logPath == null) {
+ File logDir = new File(Environment.getLegacyExternalStorageDirectory(),
+ DEFAULT_LOG_DIR);
+ if (!logDir.exists() && !logDir.mkdirs()) {
+ System.err.format("Unable to create log directory: %s\n",
+ logDir.getAbsolutePath());
+ protoFile = false;
+ return;
+ }
+ SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-hhmmss-SSS", Locale.US);
+ String fileName = String.format("log-%s.instrumentation_data_proto",
+ format.format(new Date()));
+ mLog = new File(logDir, fileName);
+ } else {
+ mLog = new File(Environment.getLegacyExternalStorageDirectory(), logPath);
+ File logDir = mLog.getParentFile();
+ if (!logDir.exists() && !logDir.mkdirs()) {
+ System.err.format("Unable to create log directory: %s\n",
+ logDir.getAbsolutePath());
+ protoFile = false;
+ return;
+ }
+ }
+ if (mLog.exists()) mLog.delete();
+ }
+ }
+
@Override
public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
Bundle results) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.startRepeatedObject(InstrumentationData.Session.TEST_STATUS);
-
- proto.writeSInt32(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
+ final long token = proto.start(InstrumentationData.Session.TEST_STATUS);
+ proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
+ proto.end(token);
- proto.endRepeatedObject(token);
- writeProtoToStdout(proto);
+ outputProto(proto);
}
@Override
@@ -197,80 +239,87 @@ public class Instrument {
Bundle results) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
-
- proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+ proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
InstrumentationData.SESSION_FINISHED);
- proto.writeSInt32(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
+ proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
+ proto.end(token);
- proto.endObject(token);
- writeProtoToStdout(proto);
+ outputProto(proto);
}
@Override
public void onError(String errorText, boolean commandError) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
-
- proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+ proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
InstrumentationData.SESSION_ABORTED);
- proto.writeString(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
+ proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
+ proto.end(token);
- proto.endObject(token);
- writeProtoToStdout(proto);
+ outputProto(proto);
}
private void writeBundle(ProtoOutputStream proto, long fieldId, Bundle bundle) {
- final long bundleToken = proto.startObject(fieldId);
+ final long bundleToken = proto.start(fieldId);
for (final String key: sorted(bundle.keySet())) {
final long entryToken = proto.startRepeatedObject(
InstrumentationData.ResultsBundle.ENTRIES);
- proto.writeString(InstrumentationData.ResultsBundleEntry.KEY, key);
+ proto.write(InstrumentationData.ResultsBundleEntry.KEY, key);
final Object val = bundle.get(key);
if (val instanceof String) {
- proto.writeString(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
(String)val);
} else if (val instanceof Byte) {
- proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT,
((Byte)val).intValue());
} else if (val instanceof Double) {
- proto.writeDouble(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE,
- ((Double)val).doubleValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE, (double)val);
} else if (val instanceof Float) {
- proto.writeFloat(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT,
- ((Float)val).floatValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT, (float)val);
} else if (val instanceof Integer) {
- proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
- ((Integer)val).intValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (int)val);
} else if (val instanceof Long) {
- proto.writeSInt64(InstrumentationData.ResultsBundleEntry.VALUE_LONG,
- ((Long)val).longValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_LONG, (long)val);
} else if (val instanceof Short) {
- proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
- ((Short)val).intValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (short)val);
} else if (val instanceof Bundle) {
writeBundle(proto, InstrumentationData.ResultsBundleEntry.VALUE_BUNDLE,
(Bundle)val);
+ } else if (val instanceof byte[]) {
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_BYTES, (byte[])val);
}
- proto.endRepeatedObject(entryToken);
+ proto.end(entryToken);
}
- proto.endObject(bundleToken);
+ proto.end(bundleToken);
}
- private void writeProtoToStdout(ProtoOutputStream proto) {
- try {
- System.out.write(proto.getBytes());
- System.out.flush();
- } catch (IOException ex) {
- System.err.println("Error writing finished response: ");
- ex.printStackTrace(System.err);
+ private void outputProto(ProtoOutputStream proto) {
+ byte[] out = proto.getBytes();
+ if (protoStd) {
+ try {
+ System.out.write(out);
+ System.out.flush();
+ } catch (IOException ex) {
+ System.err.println("Error writing finished response: ");
+ ex.printStackTrace(System.err);
+ }
+ }
+ if (protoFile) {
+ try (OutputStream os = new FileOutputStream(mLog, true)) {
+ os.write(proto.getBytes());
+ os.flush();
+ } catch (IOException ex) {
+ System.err.format("Cannot write to %s:\n", mLog.getAbsolutePath());
+ ex.printStackTrace();
+ }
}
}
}
@@ -374,7 +423,7 @@ public class Instrument {
try {
// Choose which output we will do.
- if (proto) {
+ if (protoFile || protoStd) {
reporter = new ProtoStatusReporter();
} else if (wait) {
reporter = new TextStatusReporter(rawMode);
@@ -396,7 +445,7 @@ public class Instrument {
mWm.setAnimationScale(2, 0.0f);
}
- // Figure out which component we are tring to do.
+ // Figure out which component we are trying to do.
final ComponentName cn = parseComponentName(componentNameArg);
// Choose an ABI if necessary
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index 053208313b00..2ef037143f07 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -8,44 +8,53 @@ cc_defaults {
"-O0"
],
+ local_include_dirs: [
+ "src/",
+ "src/parsers/",
+ ],
+
srcs: [
- "IncidentHelper.cpp",
- "ih_util.cpp",
+ "src/parsers/*.cpp",
+ "src/TextParserBase.cpp",
+ "src/ih_util.cpp",
],
+ generated_headers: ["gen-platform-proto-constants"],
+
shared_libs: [
"libbase",
"liblog",
- "libprotobuf-cpp-full",
+ "libprotoutil",
"libutils",
],
-
- static_libs: [
- "libplatformprotos",
- ],
}
cc_binary {
name: "incident_helper",
defaults: ["incident_helper_defaults"],
- srcs: ["main.cpp"],
+ srcs: ["src/main.cpp"],
}
cc_test {
name: "incident_helper_test",
defaults: ["incident_helper_defaults"],
+ local_include_dirs: ["src/"],
srcs: [
- "tests/IncidentHelper_test.cpp",
- "tests/ih_util_test.cpp",
+ "tests/*.cpp",
],
data: [
"testdata/*",
],
+ shared_libs: [
+ "libprotobuf-cpp-full",
+ ],
+
static_libs: [
"libgmock",
+ "libplatformprotos"
],
-} \ No newline at end of file
+}
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
deleted file mode 100644
index 7b06d42cbb55..000000000000
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "incident_helper"
-
-#include "IncidentHelper.h"
-#include "ih_util.h"
-
-#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
-#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
-#include "frameworks/base/core/proto/android/os/procrank.pb.h"
-
-#include <android-base/file.h>
-#include <unistd.h>
-#include <string>
-#include <vector>
-
-using namespace android::base;
-using namespace android::os;
-using namespace google::protobuf;
-using namespace std;
-
-
-static const string TAB_DELIMITER = "\t";
-static const string COMMA_DELIMITER = ",";
-
-static inline int toInt(const string& s) {
- return atoi(s.c_str());
-}
-
-static inline long toLong(const string& s) {
- return atol(s.c_str());
-}
-
-/**
- * Sets the given protobuf message when the field name matches one of the
- * fields. It is useful to set values to proto from table-like plain texts.
- */
-static bool
-SetTableField(::google::protobuf::Message* message, string field_name, string field_value) {
- const Descriptor* descriptor = message->GetDescriptor();
- const Reflection* reflection = message->GetReflection();
-
- const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
- switch (field->type()) {
- case FieldDescriptor::TYPE_STRING:
- reflection->SetString(message, field, field_value);
- return true;
- case FieldDescriptor::TYPE_INT64:
- reflection->SetInt64(message, field, toLong(field_value));
- return true;
- case FieldDescriptor::TYPE_UINT64:
- reflection->SetUInt64(message, field, toLong(field_value));
- return true;
- case FieldDescriptor::TYPE_INT32:
- reflection->SetInt32(message, field, toInt(field_value));
- return true;
- case FieldDescriptor::TYPE_UINT32:
- reflection->SetUInt32(message, field, toInt(field_value));
- return true;
- default:
- // Add new scalar types
- return false;
- }
-}
-
-// ================================================================================
-status_t NoopParser::Parse(const int in, const int out) const
-{
- string content;
- if (!ReadFdToString(in, &content)) {
- fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
- return -1;
- }
- if (!WriteStringToFd(content, out)) {
- fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
- return -1;
- }
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t ReverseParser::Parse(const int in, const int out) const
-{
- string content;
- if (!ReadFdToString(in, &content)) {
- fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
- return -1;
- }
- // reverse the content
- reverse(content.begin(), content.end());
- if (!WriteStringToFd(content, out)) {
- fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
- return -1;
- }
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t KernelWakesParser::Parse(const int in, const int out) const {
- Reader reader(in);
- string line;
- header_t header; // the header of /d/wakeup_sources
- record_t record; // retain each record
- int nline = 0;
-
- KernelWakeSources proto;
-
- // parse line by line
- while (reader.readLine(&line)) {
- if (line.empty()) continue;
- // parse head line
- if (nline++ == 0) {
- header = parseHeader(line, TAB_DELIMITER);
- continue;
- }
-
- // parse for each record, the line delimiter is \t only!
- record = parseRecord(line, TAB_DELIMITER);
-
- if (record.size() != header.size()) {
- // TODO: log this to incident report!
- fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
- continue;
- }
-
- WakeupSourceProto* source = proto.add_wakeup_sources();
- for (int i=0; i<(int)record.size(); i++) {
- if (!SetTableField(source, header[i], record[i])) {
- fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
- }
- }
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!proto.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t ProcrankParser::Parse(const int in, const int out) const {
- Reader reader(in);
- string line;
- header_t header; // the header of /d/wakeup_sources
- record_t record; // retain each record
- int nline = 0;
-
- Procrank proto;
-
- // parse line by line
- while (reader.readLine(&line)) {
- if (line.empty()) continue;
-
- // parse head line
- if (nline++ == 0) {
- header = parseHeader(line);
- continue;
- }
-
- if (hasPrefix(&line, "ZRAM:")) {
- proto.mutable_summary()->mutable_zram()->set_raw_text(line);
- continue;
- }
- if (hasPrefix(&line, "RAM:")) {
- proto.mutable_summary()->mutable_ram()->set_raw_text(line);
- continue;
- }
-
- record = parseRecord(line);
- if (record.size() != header.size()) {
- if (record[record.size() - 1] == "TOTAL") { // TOTAL record
- ProcessProto* total = proto.mutable_summary()->mutable_total();
- for (int i=1; i<=(int)record.size(); i++) {
- SetTableField(total, header[header.size() - i], record[record.size() - i]);
- }
- } else {
- fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
- line.c_str());
- }
- continue;
- }
-
- ProcessProto* process = proto.add_processes();
- for (int i=0; i<(int)record.size(); i++) {
- if (!SetTableField(process, header[i], record[i])) {
- fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
- }
- }
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!proto.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t PageTypeInfoParser::Parse(const int in, const int out) const {
- Reader reader(in);
- string line;
- bool migrateTypeSession = false;
- int pageBlockOrder;
- header_t blockHeader;
-
- PageTypeInfo pageTypeInfo;
-
- while (reader.readLine(&line)) {
- if (line.empty()) {
- migrateTypeSession = false;
- blockHeader.clear();
- continue;
- }
-
- if (hasPrefix(&line, "Page block order:")) {
- pageBlockOrder = toInt(line);
- pageTypeInfo.set_page_block_order(pageBlockOrder);
- continue;
- }
- if (hasPrefix(&line, "Pages per block:")) {
- pageTypeInfo.set_pages_per_block(toInt(line));
- continue;
- }
- if (hasPrefix(&line, "Free pages count per migrate type at order")) {
- migrateTypeSession = true;
- continue;
- }
- if (hasPrefix(&line, "Number of blocks type")) {
- blockHeader = parseHeader(line);
- continue;
- }
-
- record_t record = parseRecord(line, COMMA_DELIMITER);
- if (migrateTypeSession && record.size() == 3) {
- MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types();
- // expect part 0 starts with "Node"
- if (hasPrefix(&record[0], "Node")) {
- migrateType->set_node(toInt(record[0]));
- } else goto ERROR;
- // expect part 1 starts with "zone"
- if (hasPrefix(&record[1], "zone")) {
- migrateType->set_zone(record[1]);
- } else goto ERROR;
- // expect part 2 starts with "type"
- if (hasPrefix(&record[2], "type")) {
- // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
- // An example looks like:
- // header line: type 0 1 2 3 4 5 6 7 8 9 10
- // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0
- // The pageBlockOrder = 10 and it's zero-indexed. so total parts
- // are 10 + 1(zero-indexed) + 1(the type part) = 12.
- record_t pageCounts = parseRecord(record[2]);
- int pageCountsSize = pageBlockOrder + 2;
- if ((int)pageCounts.size() != pageCountsSize) goto ERROR;
-
- migrateType->set_type(pageCounts[0]);
- for (auto i=1; i<pageCountsSize; i++) {
- migrateType->add_free_pages_count(toInt(pageCounts[i]));
- }
- } else goto ERROR;
- continue;
- }
-
- if (!blockHeader.empty() && record.size() == 2) {
- BlockProto* block = pageTypeInfo.add_blocks();
-
- if (hasPrefix(&record[0], "Node")) {
- block->set_node(toInt(record[0]));
- } else goto ERROR;
-
- if (hasPrefix(&record[1], "zone")) {
- record_t blockCounts = parseRecord(record[1]);
- block->set_zone(blockCounts[0]);
- for (size_t i=0; i<blockHeader.size(); i++) {
- if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR;
- }
- } else goto ERROR;
-
- continue;
- }
-
-ERROR: // print out error for this single line and continue parsing
- fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str());
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!pageTypeInfo.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
-
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize());
- return NO_ERROR;
-}
diff --git a/cmds/incident_helper/src/TextParserBase.cpp b/cmds/incident_helper/src/TextParserBase.cpp
new file mode 100644
index 000000000000..a8f9968ee8f6
--- /dev/null
+++ b/cmds/incident_helper/src/TextParserBase.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 LOG_TAG "incident_helper"
+
+#include "TextParserBase.h"
+
+#include <android-base/file.h>
+
+using namespace android::base;
+using namespace std;
+
+// ================================================================================
+status_t NoopParser::Parse(const int in, const int out) const
+{
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+ if (!WriteStringToFd(content, out)) {
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ return -1;
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
+status_t ReverseParser::Parse(const int in, const int out) const
+{
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+ // reverse the content
+ reverse(content.begin(), content.end());
+ if (!WriteStringToFd(content, out)) {
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ return -1;
+ }
+ return NO_ERROR;
+} \ No newline at end of file
diff --git a/cmds/incident_helper/IncidentHelper.h b/cmds/incident_helper/src/TextParserBase.h
index d24d7173aa26..c41612de4eb3 100644
--- a/cmds/incident_helper/IncidentHelper.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef INCIDENT_HELPER_H
-#define INCIDENT_HELPER_H
+#ifndef TEXT_PARSER_BASE_H
+#define TEXT_PARSER_BASE_H
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -68,37 +68,4 @@ public:
virtual status_t Parse(const int in, const int out) const;
};
-/**
- * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources
- */
-class KernelWakesParser : public TextParserBase {
-public:
- KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {};
- ~KernelWakesParser() {};
-
- virtual status_t Parse(const int in, const int out) const;
-};
-
-/**
- * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype
- */
-class PageTypeInfoParser : public TextParserBase {
-public:
- PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {};
- ~PageTypeInfoParser() {};
-
- virtual status_t Parse(const int in, const int out) const;
-};
-
-/**
- * Procrank parser, parses text produced by command procrank
- */
-class ProcrankParser : public TextParserBase {
-public:
- ProcrankParser() : TextParserBase(String8("ProcrankParser")) {};
- ~ProcrankParser() {};
-
- virtual status_t Parse(const int in, const int out) const;
-};
-
-#endif // INCIDENT_HELPER_H
+#endif // TEXT_PARSER_BASE_H \ No newline at end of file
diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index 2ab4b54e193f..c7d1ca231a03 100644
--- a/cmds/incident_helper/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -87,6 +87,15 @@ bool hasPrefix(std::string* line, const char* key) {
return true;
}
+int toInt(const std::string& s) {
+ return atoi(s.c_str());
+}
+
+long long toLongLong(const std::string& s) {
+ return atoll(s.c_str());
+}
+
+// ==============================================================================
Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
Reader::Reader(const int fd, const size_t capacity)
@@ -151,3 +160,57 @@ bool Reader::ok(std::string* error) {
error->assign(mStatus);
return mStatus.empty();
}
+
+// ==============================================================================
+Table::Table(const char* names[], const uint64_t ids[], const int count)
+ :mFieldNames(names),
+ mFieldIds(ids),
+ mFieldCount(count)
+{
+}
+
+Table::~Table()
+{
+}
+
+bool
+Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+{
+ uint64_t found = 0;
+ for (int i=0; i<mFieldCount; i++) {
+ if (strcmp(name.c_str(), mFieldNames[i]) == 0) {
+ found = mFieldIds[i];
+ break;
+ }
+ }
+
+ switch (found & FIELD_TYPE_MASK) {
+ case FIELD_TYPE_DOUBLE:
+ case FIELD_TYPE_FLOAT:
+ // TODO: support parse string to float/double
+ return false;
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_BYTES:
+ proto.write(found, value);
+ break;
+ case FIELD_TYPE_INT64:
+ case FIELD_TYPE_SINT64:
+ case FIELD_TYPE_UINT64:
+ case FIELD_TYPE_FIXED64:
+ case FIELD_TYPE_SFIXED64:
+ proto.write(found, toLongLong(value));
+ break;
+ case FIELD_TYPE_BOOL:
+ case FIELD_TYPE_ENUM:
+ case FIELD_TYPE_INT32:
+ case FIELD_TYPE_SINT32:
+ case FIELD_TYPE_UINT32:
+ case FIELD_TYPE_FIXED32:
+ case FIELD_TYPE_SFIXED32:
+ proto.write(found, toInt(value));
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/src/ih_util.h
index ce5baeef0dc3..86761e93f49c 100644
--- a/cmds/incident_helper/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -21,6 +21,10 @@
#include <vector>
#include <sstream>
+#include <android/util/ProtoOutputStream.h>
+
+using namespace android::util;
+
typedef std::vector<std::string> header_t;
typedef std::vector<std::string> record_t;
typedef std::string (*trans_func) (const std::string&);
@@ -52,6 +56,12 @@ record_t parseRecord(const std::string& line, const std::string& delimiters = DE
bool hasPrefix(std::string* line, const char* key);
/**
+ * Converts string to the desired type
+ */
+int toInt(const std::string& s);
+long long toLongLong(const std::string& s);
+
+/**
* Reader class reads data from given fd in streaming fashion.
* The buffer size is controlled by capacity parameter.
*/
@@ -78,4 +88,22 @@ private:
inline bool EOR() { return mFd == -1 && mBufSize == 0; };
};
+/**
+ * The class contains a mapping between table headers to its field ids.
+ * And allow users to insert the field values to proto based on its header name.
+ */
+class Table
+{
+public:
+ Table(const char* names[], const uint64_t ids[], const int count);
+ ~Table();
+
+ bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value);
+
+private:
+ const char** mFieldNames;
+ const uint64_t* mFieldIds;
+ const int mFieldCount;
+};
+
#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/src/main.cpp
index 52ff77720d70..3da87b9c801b 100644
--- a/cmds/incident_helper/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,7 +16,9 @@
#define LOG_TAG "incident_helper"
-#include "IncidentHelper.h"
+#include "parsers/KernelWakesParser.h"
+#include "parsers/PageTypeInfoParser.h"
+#include "parsers/ProcrankParser.h"
#include <android-base/file.h>
#include <getopt.h>
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
new file mode 100644
index 000000000000..cc4a1e1ecfa2
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/kernelwake.proto.h"
+#include "ih_util.h"
+#include "KernelWakesParser.h"
+
+using namespace android::os;
+
+const std::string LINE_DELIMITER = "\t";
+
+status_t
+KernelWakesParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ record_t record; // retain each record
+ int nline = 0;
+
+ ProtoOutputStream proto;
+ Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT);
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+ // parse head line
+ if (nline++ == 0) {
+ header = parseHeader(line, LINE_DELIMITER);
+ continue;
+ }
+
+ // parse for each record, the line delimiter is \t only!
+ record = parseRecord(line, LINE_DELIMITER);
+
+ if (record.size() != header.size()) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
+ continue;
+ }
+
+ long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
+ for (int i=0; i<(int)record.size(); i++) {
+ if (!table.insertField(proto, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
+ }
+ proto.end(token);
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.h b/cmds/incident_helper/src/parsers/KernelWakesParser.h
new file mode 100644
index 000000000000..aabab7c64a4f
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.h
@@ -0,0 +1,33 @@
+/*
+ * 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 KERNEL_WAKES_PARSER_H
+#define KERNEL_WAKES_PARSER_H
+
+#include "TextParserBase.h"
+
+/**
+ * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources
+ */
+class KernelWakesParser : public TextParserBase {
+public:
+ KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {};
+ ~KernelWakesParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // KERNEL_WAKES_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
new file mode 100644
index 000000000000..6047bd189b95
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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 LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/pagetypeinfo.proto.h"
+#include "ih_util.h"
+#include "PageTypeInfoParser.h"
+
+using namespace android::os;
+
+const std::string LINE_DELIMITER = ",";
+
+status_t
+PageTypeInfoParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ bool migrateTypeSession = false;
+ int pageBlockOrder;
+ header_t blockHeader;
+
+ ProtoOutputStream proto;
+ Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT);
+
+ while (reader.readLine(&line)) {
+ if (line.empty()) {
+ migrateTypeSession = false;
+ blockHeader.clear();
+ continue;
+ }
+
+ if (hasPrefix(&line, "Page block order:")) {
+ pageBlockOrder = toInt(line);
+ proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
+ continue;
+ }
+ if (hasPrefix(&line, "Pages per block:")) {
+ proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
+ continue;
+ }
+ if (hasPrefix(&line, "Free pages count per migrate type at order")) {
+ migrateTypeSession = true;
+ continue;
+ }
+ if (hasPrefix(&line, "Number of blocks type")) {
+ blockHeader = parseHeader(line);
+ continue;
+ }
+
+ record_t record = parseRecord(line, LINE_DELIMITER);
+ if (migrateTypeSession && record.size() == 3) {
+ long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
+ // expect part 0 starts with "Node"
+ if (hasPrefix(&record[0], "Node")) {
+ proto.write(MigrateTypeProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+ // expect part 1 starts with "zone"
+ if (hasPrefix(&record[1], "zone")) {
+ proto.write(MigrateTypeProto::ZONE, record[1]);
+ } else return BAD_VALUE;
+ // expect part 2 starts with "type"
+ if (hasPrefix(&record[2], "type")) {
+ // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
+ // An example looks like:
+ // header line: type 0 1 2 3 4 5 6 7 8 9 10
+ // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0
+ // The pageBlockOrder = 10 and it's zero-indexed. so total parts
+ // are 10 + 1(zero-indexed) + 1(the type part) = 12.
+ record_t pageCounts = parseRecord(record[2]);
+ int pageCountsSize = pageBlockOrder + 2;
+ if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE;
+
+ proto.write(MigrateTypeProto::TYPE, pageCounts[0]);
+ for (auto i=1; i<pageCountsSize; i++) {
+ proto.write(MigrateTypeProto::FREE_PAGES_COUNT, toInt(pageCounts[i]));
+ }
+ } else return BAD_VALUE;
+
+ proto.end(token);
+ } else if (!blockHeader.empty() && record.size() == 2) {
+ long long token = proto.start(PageTypeInfo::BLOCKS);
+ if (hasPrefix(&record[0], "Node")) {
+ proto.write(BlockProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+
+ if (hasPrefix(&record[1], "zone")) {
+ record_t blockCounts = parseRecord(record[1]);
+ proto.write(BlockProto::ZONE, blockCounts[0]);
+
+ for (size_t i=0; i<blockHeader.size(); i++) {
+ if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) {
+ return BAD_VALUE;
+ }
+ }
+ } else return BAD_VALUE;
+ proto.end(token);
+ }
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+} \ No newline at end of file
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.h b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h
new file mode 100644
index 000000000000..fb84d912a5f2
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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 PAGE_TYPE_INFO_PARSER_H
+#define PAGE_TYPE_INFO_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype
+ */
+class PageTypeInfoParser : public TextParserBase {
+public:
+ PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {};
+ ~PageTypeInfoParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PAGE_TYPE_INFO_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
new file mode 100644
index 000000000000..93f970f820d9
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/procrank.proto.h"
+#include "ih_util.h"
+#include "ProcrankParser.h"
+
+using namespace android::os;
+
+status_t
+ProcrankParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ record_t record; // retain each record
+ int nline = 0;
+
+ ProtoOutputStream proto;
+ Table table(ProcessProto::_FIELD_NAMES, ProcessProto::_FIELD_IDS, ProcessProto::_FIELD_COUNT);
+ string zram, ram, total;
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ // parse head line
+ if (nline++ == 0) {
+ header = parseHeader(line);
+ continue;
+ }
+
+ if (hasPrefix(&line, "ZRAM:")) {
+ zram = line;
+ continue;
+ }
+ if (hasPrefix(&line, "RAM:")) {
+ ram = line;
+ continue;
+ }
+
+ record = parseRecord(line);
+ if (record.size() != header.size()) {
+ if (record[record.size() - 1] == "TOTAL") { // TOTAL record
+ total = line;
+ } else {
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
+ line.c_str());
+ }
+ continue;
+ }
+
+ long long token = proto.start(Procrank::PROCESSES);
+ for (int i=0; i<(int)record.size(); i++) {
+ if (!table.insertField(proto, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
+ }
+ proto.end(token);
+ }
+
+ // add summary
+ long long token = proto.start(Procrank::SUMMARY);
+ if (!total.empty()) {
+ record = parseRecord(total);
+ long long token = proto.start(SummaryProto::TOTAL);
+ for (int i=(int)record.size(); i>0; i--) {
+ table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
+ }
+ proto.end(token);
+ }
+ if (!zram.empty()) {
+ long long token = proto.start(SummaryProto::ZRAM);
+ proto.write(ZramProto::RAW_TEXT, zram);
+ proto.end(token);
+ }
+ if (!ram.empty()) {
+ long long token = proto.start(SummaryProto::RAM);
+ proto.write(RamProto::RAW_TEXT, ram);
+ proto.end(token);
+ }
+ proto.end(token);
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.h b/cmds/incident_helper/src/parsers/ProcrankParser.h
new file mode 100644
index 000000000000..5d0ee48aa5b1
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.h
@@ -0,0 +1,35 @@
+/*
+ * 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 PROCRANK_PARSER_H
+#define PROCRANK_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Procrank parser, parses text produced by command procrank
+ */
+class ProcrankParser : public TextParserBase {
+public:
+ ProcrankParser() : TextParserBase(String8("ProcrankParser")) {};
+ ~ProcrankParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PROCRANK_PARSER_H
diff --git a/cmds/incident_helper/testdata/kernel_wakeups_short.txt b/cmds/incident_helper/testdata/kernel_wakeups_short.txt
new file mode 100644
index 000000000000..a51926e70def
--- /dev/null
+++ b/cmds/incident_helper/testdata/kernel_wakeups_short.txt
@@ -0,0 +1,3 @@
+name active_count last_change
+ab 8 123456123456
+df 143 0
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
new file mode 100644
index 000000000000..a8fa62088450
--- /dev/null
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "KernelWakesParser.h"
+
+#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class KernelWakesParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(KernelWakesParserTest, Short) {
+ const string testFile = kTestDataPath + "kernel_wakeups_short.txt";
+ KernelWakesParser parser;
+ KernelWakeSources expected;
+
+ WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ record1->set_name("ab");
+ record1->set_active_count(8);
+ record1->set_last_change(123456123456LL);
+
+ WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ record2->set_name("df");
+ record2->set_active_count(143);
+ record2->set_last_change(0LL);
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
+
+TEST_F(KernelWakesParserTest, Normal) {
+ const string testFile = kTestDataPath + "kernel_wakeups.txt";
+ KernelWakesParser parser;
+ KernelWakeSources expected;
+
+ WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ record1->set_name("ipc000000ab_ATFWD-daemon");
+ record1->set_active_count(8);
+ record1->set_event_count(8);
+ record1->set_wakeup_count(0);
+ record1->set_expire_count(0);
+ record1->set_active_since(0l);
+ record1->set_total_time(0l);
+ record1->set_max_time(0l);
+ record1->set_last_change(131348LL);
+ record1->set_prevent_suspend_time(0LL);
+
+ WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ record2->set_name("ipc000000aa_ATFWD-daemon");
+ record2->set_active_count(143);
+ record2->set_event_count(143);
+ record2->set_wakeup_count(0);
+ record2->set_expire_count(0);
+ record2->set_active_since(0l);
+ record2->set_total_time(123l);
+ record2->set_max_time(3l);
+ record2->set_last_change(2067286206LL);
+ record2->set_prevent_suspend_time(0LL);
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
new file mode 100644
index 000000000000..de64e70c80c7
--- /dev/null
+++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
@@ -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.
+ */
+
+#include "PageTypeInfoParser.h"
+
+#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class PageTypeInfoParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(PageTypeInfoParserTest, Success) {
+ const string testFile = kTestDataPath + "pagetypeinfo.txt";
+ PageTypeInfoParser parser;
+ PageTypeInfo expected;
+
+ expected.set_page_block_order(10);
+ expected.set_pages_per_block(1024);
+
+ MigrateTypeProto* mt1 = expected.add_migrate_types();
+ mt1->set_node(0);
+ mt1->set_zone("DMA");
+ mt1->set_type("Unmovable");
+ int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0};
+ for (auto i=0; i<11; i++) {
+ mt1->add_free_pages_count(arr1[i]);
+ }
+
+ MigrateTypeProto* mt2 = expected.add_migrate_types();
+ mt2->set_node(0);
+ mt2->set_zone("Normal");
+ mt2->set_type("Reclaimable");
+ int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0};
+ for (auto i=0; i<11; i++) {
+ mt2->add_free_pages_count(arr2[i]);
+ }
+
+ BlockProto* block1 = expected.add_blocks();
+ block1->set_node(0);
+ block1->set_zone("DMA");
+ block1->set_unmovable(74);
+ block1->set_reclaimable(9);
+ block1->set_movable(337);
+ block1->set_cma(41);
+ block1->set_reserve(1);
+ block1->set_isolate(0);
+
+
+ BlockProto* block2 = expected.add_blocks();
+ block2->set_node(0);
+ block2->set_zone("Normal");
+ block2->set_unmovable(70);
+ block2->set_reclaimable(12);
+ block2->set_movable(423);
+ block2->set_cma(0);
+ block2->set_reserve(1);
+ block2->set_isolate(0);
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+} \ No newline at end of file
diff --git a/cmds/incident_helper/tests/IncidentHelper_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp
index c44a163efa11..e86647ad479b 100644
--- a/cmds/incident_helper/tests/IncidentHelper_test.cpp
+++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-#include "IncidentHelper.h"
+#include "ProcrankParser.h"
-#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
-#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
#include "frameworks/base/core/proto/android/os/procrank.pb.h"
#include <android-base/file.h>
@@ -38,7 +36,7 @@ using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStderr;
using ::testing::internal::GetCapturedStdout;
-class IncidentHelperTest : public Test {
+class ProcrankParserTest : public Test {
public:
virtual void SetUp() override {
ASSERT_TRUE(tf.fd != -1);
@@ -58,57 +56,7 @@ protected:
const string kTestDataPath = kTestPath + "/testdata/";
};
-TEST_F(IncidentHelperTest, ReverseParser) {
- ReverseParser parser;
- TemporaryFile tf;
-
- ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFile("TestData", tf.path, false));
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(tf.fd, STDOUT_FILENO));
- EXPECT_THAT(GetCapturedStdout(), StrEq("ataDtseT"));
-}
-
-TEST_F(IncidentHelperTest, KernelWakesParser) {
- const string testFile = kTestDataPath + "kernel_wakeups.txt";
- KernelWakesParser parser;
- KernelWakeSources expected;
-
- WakeupSourceProto* record1 = expected.add_wakeup_sources();
- record1->set_name("ipc000000ab_ATFWD-daemon");
- record1->set_active_count(8);
- record1->set_event_count(8);
- record1->set_wakeup_count(0);
- record1->set_expire_count(0);
- record1->set_active_since(0l);
- record1->set_total_time(0l);
- record1->set_max_time(0l);
- record1->set_last_change(131348l);
- record1->set_prevent_suspend_time(0l);
-
- WakeupSourceProto* record2 = expected.add_wakeup_sources();
- record2->set_name("ipc000000aa_ATFWD-daemon");
- record2->set_active_count(143);
- record2->set_event_count(143);
- record2->set_wakeup_count(0);
- record2->set_expire_count(0);
- record2->set_active_since(0l);
- record2->set_total_time(123l);
- record2->set_max_time(3l);
- record2->set_last_change(2067286206l);
- record2->set_prevent_suspend_time(0l);
-
- int fd = open(testFile.c_str(), O_RDONLY);
- ASSERT_TRUE(fd != -1);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
- close(fd);
-}
-
-TEST_F(IncidentHelperTest, ProcrankParser) {
+TEST_F(ProcrankParserTest, HasSwapInfo) {
const string testFile = kTestDataPath + "procrank.txt";
ProcrankParser parser;
Procrank expected;
@@ -160,7 +108,7 @@ TEST_F(IncidentHelperTest, ProcrankParser) {
close(fd);
}
-TEST_F(IncidentHelperTest, ProcrankParserShortHeader) {
+TEST_F(ProcrankParserTest, NoSwapInfo) {
const string testFile = kTestDataPath + "procrank_short.txt";
ProcrankParser parser;
Procrank expected;
@@ -197,59 +145,3 @@ TEST_F(IncidentHelperTest, ProcrankParserShortHeader) {
EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
close(fd);
}
-
-TEST_F(IncidentHelperTest, PageTypeInfoParser) {
- const string testFile = kTestDataPath + "pagetypeinfo.txt";
- PageTypeInfoParser parser;
- PageTypeInfo expected;
-
- expected.set_page_block_order(10);
- expected.set_pages_per_block(1024);
-
- MigrateTypeProto* mt1 = expected.add_migrate_types();
- mt1->set_node(0);
- mt1->set_zone("DMA");
- mt1->set_type("Unmovable");
- int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0};
- for (auto i=0; i<11; i++) {
- mt1->add_free_pages_count(arr1[i]);
- }
-
- MigrateTypeProto* mt2 = expected.add_migrate_types();
- mt2->set_node(0);
- mt2->set_zone("Normal");
- mt2->set_type("Reclaimable");
- int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0};
- for (auto i=0; i<11; i++) {
- mt2->add_free_pages_count(arr2[i]);
- }
-
- BlockProto* block1 = expected.add_blocks();
- block1->set_node(0);
- block1->set_zone("DMA");
- block1->set_unmovable(74);
- block1->set_reclaimable(9);
- block1->set_movable(337);
- block1->set_cma(41);
- block1->set_reserve(1);
- block1->set_isolate(0);
-
-
- BlockProto* block2 = expected.add_blocks();
- block2->set_node(0);
- block2->set_zone("Normal");
- block2->set_unmovable(70);
- block2->set_reclaimable(12);
- block2->set_movable(423);
- block2->set_cma(0);
- block2->set_reserve(1);
- block2->set_isolate(0);
-
- int fd = open(testFile.c_str(), O_RDONLY);
- ASSERT_TRUE(fd != -1);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
- close(fd);
-} \ No newline at end of file
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index d926ea7a9c87..77ae1a7ec730 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -87,12 +87,12 @@ PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec)
// current field is message type and its sub-fields have extra privacy policies
uint32_t msgSize = mData.readRawVarint();
EncodedBuffer::Pointer start = mData.rp()->copy();
+ long long token = mProto.start(policy->EncodedFieldId());
while (mData.rp()->pos() - start.pos() != msgSize) {
- long long token = mProto.start(policy->EncodedFieldId());
status_t err = stripField(policy, spec);
if (err != NO_ERROR) return err;
- mProto.end(token);
}
+ mProto.end(token);
return NO_ERROR;
}
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
index da82b008fc9b..dfd2312df668 100644
--- a/cmds/incidentd/src/section_list.h
+++ b/cmds/incidentd/src/section_list.h
@@ -30,7 +30,7 @@ extern const Section* SECTION_LIST[];
* This is the mapping of section IDs to each section's privacy policy.
* The section IDs are guaranteed in ascending order, not NULL-terminated since size is provided.
*/
-extern const Privacy* PRIVACY_POLICY_LIST[];
+extern const Privacy** PRIVACY_POLICY_LIST;
extern const int PRIVACY_POLICY_COUNT;
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 8f6e35548e78..84a2a825b4c3 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -260,3 +260,13 @@ TEST_F(PrivacyBufferTest, BadDataInNestedMessage) {
PrivacySpec spec;
ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE);
}
+
+TEST_F(PrivacyBufferTest, SelfRecursionMessage) {
+ string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5;
+ writeToFdBuffer(input);
+ Privacy* field5 = create_message_privacy(5, NULL);
+ Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), field5, NULL };
+ field5->children = list;
+ string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2;
+ assertStrip(EXPLICIT, expected, field5);
+}
diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp
index e47b61cf3854..4acc4298ab82 100644
--- a/cmds/incidentd/tests/section_list.cpp
+++ b/cmds/incidentd/tests/section_list.cpp
@@ -20,9 +20,11 @@ Privacy* list[] = {
Privacy field_0 { 0, 11, list, EXPLICIT, NULL };
Privacy field_1 { 1, 9, NULL, AUTOMATIC, NULL };
-const Privacy* PRIVACY_POLICY_LIST[] = {
+Privacy* final_list[] = {
&field_0,
&field_1
};
-const int PRIVACY_POLICY_COUNT = 2; \ No newline at end of file
+const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(final_list);
+
+const int PRIVACY_POLICY_COUNT = 2;
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 29433f3fc14f..9490880afe0f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -157,7 +157,8 @@ public final class Pm {
}
static final class MyShellCallback extends ShellCallback {
- @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ @Override public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext,
+ String mode) {
File file = new File(path);
final ParcelFileDescriptor fd;
try {
diff --git a/cmds/statsd/.clang-format b/cmds/statsd/.clang-format
index 3d64beeecda6..cead3a079435 100644
--- a/cmds/statsd/.clang-format
+++ b/cmds/statsd/.clang-format
@@ -12,3 +12,6 @@ IndentWidth: 4
PointerAlignment: Left
TabWidth: 4
AccessModifierOffset: -4
+IncludeCategories:
+ - Regex: '^"Log\.h"'
+ Priority: -1
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index db634d4a6601..54ade3563851 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -42,6 +42,9 @@ statsd_common_src := \
src/metrics/EventMetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
src/metrics/DurationMetricProducer.cpp \
+ src/metrics/duration_helper/OringDurationTracker.cpp \
+ src/metrics/duration_helper/MaxDurationTracker.cpp \
+ src/metrics/ValueMetricProducer.cpp \
src/metrics/MetricsManager.cpp \
src/metrics/metrics_manager_util.cpp \
src/packages/UidMap.cpp \
@@ -103,7 +106,8 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
+LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
+ libgtest_prod
LOCAL_MODULE_CLASS := EXECUTABLES
@@ -142,7 +146,9 @@ LOCAL_SRC_FILES := \
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
tests/MetricsManager_test.cpp \
- tests/UidMap_test.cpp
+ tests/UidMap_test.cpp \
+ tests/OringDurationTracker_test.cpp \
+ tests/MaxDurationTracker_test.cpp
LOCAL_STATIC_LIBRARIES := \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 56d4e4d940c2..c0cedb1b467e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -46,6 +46,7 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
// pass the event to metrics managers.
for (auto& pair : mMetricsManagers) {
pair.second->onLogEvent(msg);
+ flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
}
}
@@ -59,6 +60,7 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig
unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
if (newMetricsManager->isConfigValid()) {
+ mUidMap->OnConfigUpdated(key);
mMetricsManagers[key] = std::move(newMetricsManager);
// Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
ALOGD("StatsdConfig valid");
@@ -68,14 +70,27 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig
}
}
-vector<StatsLogReport> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
+ConfigMetricsReport StatsLogProcessor::onDumpReport(const ConfigKey& key) {
+ ConfigMetricsReport report;
+
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end()) {
ALOGW("Config source %s does not exist", key.ToString().c_str());
- return vector<StatsLogReport>();
+ return report;
}
- return it->second->onDumpReport();
+ auto set_key = report.mutable_config_key();
+ set_key->set_uid(key.GetUid());
+ set_key->set_name(key.GetName());
+ for (auto m : it->second->onDumpReport()) {
+ // Transfer the vector of StatsLogReport into a field
+ // TODO: perhaps we just have bytes being returned from onDumpReport and transfer bytes
+ auto dest = report.add_metrics();
+ *dest = m;
+ }
+ auto temp = mUidMap->getOutput(key);
+ report.set_allocated_uid_map(&temp);
+ return report;
}
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
@@ -83,42 +98,42 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
if (it != mMetricsManagers.end()) {
it->second->finish();
mMetricsManagers.erase(it);
+ mUidMap->OnConfigRemoved(key);
+ }
+ auto flushTime = mLastFlushTimes.find(key);
+ if (flushTime != mLastFlushTimes.end()) {
+ mLastFlushTimes.erase(flushTime);
}
}
-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(uint64_t timestampNs,
+ const ConfigKey& key,
+ const unique_ptr<MetricsManager>& metricsManager) {
+ auto lastFlushNs = mLastFlushTimes.find(key);
+ if (lastFlushNs != mLastFlushTimes.end()) {
+ if (timestampNs - lastFlushNs->second < kMinFlushPeriod) {
+ return;
+ }
+ }
-void StatsLogProcessor::flushIfNecessary(const EventMetricData& eventMetricData) {
- if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) {
- flush();
+ size_t totalBytes = metricsManager->byteSize();
+ if (totalBytes > kMaxSerializedBytes) {
+ flush();
+ mLastFlushTimes[key] = std::move(timestampNs);
}
}
void StatsLogProcessor::flush() {
+ // TODO: Take ConfigKey as an argument and flush metrics related to the
+ // ConfigKey. Also, create a wrapper that holds a repeated field of
+ // StatsLogReport's.
+ /*
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
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 9cd74caf095e..0083827a347d 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -42,7 +42,7 @@ public:
void OnConfigRemoved(const ConfigKey& key);
// TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
- std::vector<StatsLogReport> onDumpReport(const ConfigKey& key);
+ ConfigMetricsReport onDumpReport(const ConfigKey& key);
/* Request a flush through a binder call. */
void flush();
@@ -50,6 +50,8 @@ public:
private:
std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;
+ std::unordered_map<ConfigKey, long> mLastFlushTimes;
+
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.
@@ -59,25 +61,17 @@ private:
*/
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 callback clients if true. */
- void flushIfNecessary(const EventMetricData& eventMetricData);
-
- /* Append event metric data to StatsLogReport. */
- void addEventMetricData(const EventMetricData& eventMetricData);
+ void flushIfNecessary(uint64_t timestampNs,
+ const ConfigKey& key,
+ const unique_ptr<MetricsManager>& metricsManager);
std::function<void(const vector<uint8_t>&)> mPushLog;
+
+ /* Minimum period between two flushes in nanoseconds. Currently set to 10
+ * minutes. */
+ static const unsigned long long kMinFlushPeriod = 600 * NS_PER_SEC;
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 604753ef54a0..edb1a0f94e2b 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -65,7 +65,6 @@ void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
StatsService::StatsService(const sp<Looper>& handlerLooper)
: mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better
{
- mStatsPullerManager = new StatsPullerManager();
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) {
@@ -231,6 +230,14 @@ void StatsService::print_cmd_help(FILE* out) {
fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
fprintf(out, " uid is used.\n");
fprintf(out, " NAME The per-uid name to use\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME\n");
+ fprintf(out, " Dump all metric data for a configuration.\n");
+ fprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
+ fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
+ fprintf(out, " calling uid is used.\n");
+ fprintf(out, " NAME The name of the configuration\n");
}
status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
@@ -312,7 +319,7 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
// TODO: What if this isn't a binder call? Should we fail?
- name.assign(args[2].c_str(), args[2].size());
+ name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
// If it's a userdebug or eng build, then the shell user can
@@ -366,7 +373,7 @@ status_t StatsService::cmd_print_uid_map(FILE* out) {
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
- auto stats = mStatsPullerManager->Pull(s);
+ auto stats = m_stats_puller_manager.Pull(s, time(nullptr));
for (const auto& it : stats) {
fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
@@ -433,8 +440,9 @@ Status StatsService::informPollAlarmFired() {
"Only system uid can call informPollAlarmFired");
}
+ m_stats_puller_manager.OnAlarmFired();
+
if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
- // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 7f046584b2d0..3930d319ce8d 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,7 +27,6 @@
#include <android/os/IStatsCallbacks.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
-#include <binder/IShellCallback.h>
#include <utils/Looper.h>
#include <deque>
@@ -158,7 +157,7 @@ private:
/**
* Fetches external metrics.
*/
- sp<StatsPullerManager> mStatsPullerManager;
+ StatsPullerManager& m_stats_puller_manager = StatsPullerManager::GetInstance();
/**
* Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index f56c15a37086..953bcb3d67a4 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -16,7 +16,6 @@
#define DEBUG true // STOPSHIP if true
#include "Log.h"
-
#include "CombinationConditionTracker.h"
#include <log/logprint.h>
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index fc88a88f4d63..dbdb3b7d31d7 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -46,8 +46,6 @@ public:
const std::vector<sp<ConditionTracker>>& allConditions,
std::vector<ConditionState>& conditionCache) override;
- void addDimensions(const std::vector<KeyMatcher>& keyMatchers) override{};
-
private:
LogicalOperation mLogicalOperation;
// Store index of the children Conditions.
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 055b4784f72c..bb5ddeb4ec2b 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -16,8 +16,6 @@
#pragma once
-#include "Log.h"
-
#include "condition/condition_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
@@ -103,8 +101,6 @@ public:
mSliced = mSliced | sliced;
}
- virtual void addDimensions(const std::vector<KeyMatcher>& keyMatchers) = 0;
-
protected:
// We don't really need the string name, but having a name here makes log messages
// easy to debug.
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4889b64de2ed..1a01afa7fbdc 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -27,19 +27,23 @@ namespace statsd {
// Held by MetricProducer, to query a condition state with input defined in EventConditionLink.
class ConditionWizard : public virtual android::RefBase {
public:
+ ConditionWizard(){}; // for testing
ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
: mAllConditions(conditionTrackers){};
+ virtual ~ConditionWizard(){};
+
// Query condition state, for a ConditionTracker at [conditionIndex], with [conditionParameters]
// [conditionParameters] mapping from condition name to the HashableDimensionKey to query the
// condition.
// The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
- ConditionState query(const int conditionIndex,
- const std::map<std::string, HashableDimensionKey>& conditionParameters);
+ virtual ConditionState query(
+ const int conditionIndex,
+ const std::map<std::string, HashableDimensionKey>& conditionParameters);
private:
- std::vector<sp<ConditionTracker>>& mAllConditions;
+ std::vector<sp<ConditionTracker>> mAllConditions;
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index aff476814c94..b691faea205d 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -74,6 +74,13 @@ SimpleConditionTracker::SimpleConditionTracker(
mStopAllLogMatcherIndex = -1;
}
+ mDimension.insert(mDimension.begin(), simpleCondition.dimension().begin(),
+ simpleCondition.dimension().end());
+
+ if (mDimension.size() > 0) {
+ mSliced = true;
+ }
+
mInitialized = true;
}
@@ -98,12 +105,6 @@ void print(unordered_map<HashableDimensionKey, ConditionState>& conditions, cons
}
}
-void SimpleConditionTracker::addDimensions(const std::vector<KeyMatcher>& keyMatchers) {
- VLOG("Added dimensions size %lu", (unsigned long)keyMatchers.size());
- mDimensionsList.push_back(keyMatchers);
- mSliced = true;
-}
-
bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
const vector<MatchingState>& eventMatcherValues,
const vector<sp<ConditionTracker>>& mAllConditions,
@@ -157,18 +158,15 @@ bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
// TODO: handle stop all; all dimension should be cleared.
}
- if (mDimensionsList.size() > 0) {
- for (size_t i = 0; i < mDimensionsList.size(); i++) {
- const auto& dim = mDimensionsList[i];
- vector<KeyValuePair> key = getDimensionKey(event, dim);
- HashableDimensionKey hashableKey = getHashableKey(key);
- if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
- mSlicedConditionState[hashableKey] != newCondition) {
- slicedChanged = true;
- mSlicedConditionState[hashableKey] = newCondition;
- }
- VLOG("key: %s %d", hashableKey.c_str(), newCondition);
+
+ if (mDimension.size() > 0) {
+ HashableDimensionKey hashableKey = getHashableKey(getDimensionKey(event, mDimension));
+ if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
+ mSlicedConditionState[hashableKey] != newCondition) {
+ slicedChanged = true;
+ mSlicedConditionState[hashableKey] = newCondition;
}
+ VLOG("key: %s %d", hashableKey.c_str(), newCondition);
// dump all dimensions for debugging
if (DEBUG) {
print(mSlicedConditionState, mName);
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 1f357f059aab..b72157baf1e2 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -27,8 +27,6 @@ namespace statsd {
class SimpleConditionTracker : public virtual ConditionTracker {
public:
- // dimensions is a vector of vector because for one single condition, different metrics may be
- // interested in slicing in different ways. one vector<KeyMatcher> defines one type of slicing.
SimpleConditionTracker(const std::string& name, const int index,
const SimpleCondition& simpleCondition,
const std::unordered_map<std::string, int>& trackerNameIndexMap);
@@ -51,8 +49,6 @@ public:
const std::vector<sp<ConditionTracker>>& allConditions,
std::vector<ConditionState>& conditionCache) override;
- void addDimensions(const std::vector<KeyMatcher>& keyMatchers) override;
-
private:
// The index of the LogEventMatcher which defines the start.
int mStartLogMatcherIndex;
@@ -66,8 +62,11 @@ private:
// The index of the LogEventMatcher which defines the stop all.
int mStopAllLogMatcherIndex;
- // Different metrics may subscribe to different types of slicings. So it's a vector of vector.
- std::vector<std::vector<KeyMatcher>> mDimensionsList;
+ // The dimension defines at the atom level, how start and stop should match.
+ // e.g., APP_IN_FOREGROUND, the dimension should be the uid field. Each "start" and
+ // "stop" tells you the state change of a particular app. Without this dimension, this
+ // condition does not make sense.
+ std::vector<KeyMatcher> mDimension;
// Keep the map from the internal HashableDimensionKey to std::vector<KeyValuePair>
// that StatsLogReport wants.
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index c16971aeb8a8..88127194753f 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -118,9 +118,10 @@ static StatsdConfig build_fake_config() {
StatsdConfig config;
config.set_config_id(12345L);
- int WAKE_LOCK_TAG_ID = 11;
+ int WAKE_LOCK_TAG_ID = 1111; // put a fake id here to make testing easier.
int WAKE_LOCK_UID_KEY_ID = 1;
- int WAKE_LOCK_STATE_KEY = 3;
+ int WAKE_LOCK_NAME_KEY = 3;
+ int WAKE_LOCK_STATE_KEY = 4;
int WAKE_LOCK_ACQUIRE_VALUE = 1;
int WAKE_LOCK_RELEASE_VALUE = 0;
@@ -138,6 +139,9 @@ static StatsdConfig build_fake_config() {
int UID_PROCESS_STATE_TAG_ID = 27;
int UID_PROCESS_STATE_UID_KEY = 1;
+ int KERNEL_WAKELOCK_TAG_ID = 41;
+ int KERNEL_WAKELOCK_NAME_KEY = 4;
+
// Count Screen ON events.
CountMetric* metric = config.add_count_metric();
metric->set_metric_id(1);
@@ -180,24 +184,67 @@ 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);
- // Duration of an app holding wl, while screen on and app in background
+ // Duration of an app holding any wl, while screen on and app in background, slice by uid
DurationMetric* durationMetric = config.add_duration_metric();
durationMetric->set_metric_id(5);
- durationMetric->set_start("APP_GET_WL");
- durationMetric->set_stop("APP_RELEASE_WL");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+ durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
+ link = durationMetric->add_links();
+ link->set_condition("APP_IS_BACKGROUND");
+ link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+ link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+
+ // max Duration of an app holding any wl, while screen on and app in background, slice by uid
+ durationMetric = config.add_duration_metric();
+ durationMetric->set_metric_id(6);
+ durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+ keyMatcher = durationMetric->add_dimension();
+ keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+ durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+ // Duration of an app holding any wl, while screen on and app in background
+ durationMetric = config.add_duration_metric();
+ durationMetric->set_metric_id(7);
+ durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+ durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
+ link = durationMetric->add_links();
+ link->set_condition("APP_IS_BACKGROUND");
+ link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+ link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+
+ // Duration of screen on time.
+ durationMetric = config.add_duration_metric();
+ durationMetric->set_metric_id(8);
+ durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+ durationMetric->set_what("SCREEN_IS_ON");
+
+ // Value metric to count KERNEL_WAKELOCK when screen turned on
+ ValueMetric* valueMetric = config.add_value_metric();
+ valueMetric->set_metric_id(6);
+ valueMetric->set_what("KERNEL_WAKELOCK");
+ valueMetric->set_value_field(1);
+ valueMetric->set_condition("SCREEN_IS_ON");
+ keyMatcher = valueMetric->add_dimension();
+ keyMatcher->set_key(KERNEL_WAKELOCK_NAME_KEY);
+ // This is for testing easier. We should never set bucket size this small.
+ valueMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
+
// Add an EventMetric to log process state change events.
EventMetric* eventMetric = config.add_event_metric();
- eventMetric->set_metric_id(6);
+ eventMetric->set_metric_id(9);
eventMetric->set_what("SCREEN_TURNED_ON");
// Event matchers............
@@ -272,6 +319,8 @@ static StatsdConfig build_fake_config() {
simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("APP_GOES_BACKGROUND");
simpleCondition->set_stop("APP_GOES_FOREGROUND");
+ KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
+ condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
condition = config.add_condition();
condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
@@ -280,6 +329,16 @@ static StatsdConfig build_fake_config() {
combination_condition->add_condition("APP_IS_BACKGROUND");
combination_condition->add_condition("SCREEN_IS_ON");
+ condition = config.add_condition();
+ condition->set_name("WL_STATE_PER_APP_PER_NAME");
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("APP_GET_WL");
+ simpleCondition->set_stop("APP_RELEASE_WL");
+ KeyMatcher* condition_dimension = simpleCondition->add_dimension();
+ condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+ condition_dimension = simpleCondition->add_dimension();
+ condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
+
return config;
}
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.cpp b/cmds/statsd/src/external/KernelWakelockPuller.cpp
index ee072f80aa4a..00259a83d3db 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.cpp
+++ b/cmds/statsd/src/external/KernelWakelockPuller.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
+#define DEBUG true
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
#include <binder/IPCThreadState.h>
#include <private/android_filesystem_config.h>
+#include "KernelWakelockPuller.h"
#include "StatsService.h"
-#include "external/KernelWakelockPuller.h"
-#include "external/StatsPuller.h"
using namespace android;
using namespace android::base;
@@ -33,11 +33,11 @@ namespace android {
namespace os {
namespace statsd {
-const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 20;
+const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 1004;
// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
// let StatsCompanionService handle that and send the data back.
-vector<StatsLogEventWrapper> KernelWakelockPuller::pull() {
+vector<StatsLogEventWrapper> KernelWakelockPuller::Pull() {
sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
vector<StatsLogEventWrapper> returned_value;
if (statsCompanion != NULL) {
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.h b/cmds/statsd/src/external/KernelWakelockPuller.h
index c12806c1fc47..cc8059d80601 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.h
+++ b/cmds/statsd/src/external/KernelWakelockPuller.h
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#ifndef STATSD_KERNELWAKELOCKPULLER_H
-#define STATSD_KERNELWAKELOCKPULLER_H
+#pragma once
#include <utils/String16.h>
-#include "external/StatsPuller.h"
+#include "StatsPuller.h"
namespace android {
namespace os {
@@ -29,11 +28,9 @@ public:
// a number of stats need to be pulled from StatsCompanionService
//
const static int PULL_CODE_KERNEL_WAKELOCKS;
- vector<StatsLogEventWrapper> pull() override;
+ vector<StatsLogEventWrapper> Pull() override;
};
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // STATSD_KERNELWAKELOCKPULLER_H
diff --git a/cmds/statsd/src/external/PullDataReceiver.h b/cmds/statsd/src/external/PullDataReceiver.h
new file mode 100644
index 000000000000..0d505cb49e8f
--- /dev/null
+++ b/cmds/statsd/src/external/PullDataReceiver.h
@@ -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.
+ */
+#pragma once
+
+#include <utils/String16.h>
+#include <unordered_map>
+#include <utils/RefBase.h>
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PullDataReceiver : virtual public RefBase{
+ public:
+ virtual ~PullDataReceiver() {}
+ virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 66556291e976..774e7f093087 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef STATSD_STATSPULLER_H
-#define STATSD_STATSPULLER_H
+#pragma once
#include <android/os/StatsLogEventWrapper.h>
#include <utils/String16.h>
@@ -32,11 +31,9 @@ class StatsPuller {
public:
virtual ~StatsPuller(){};
- virtual vector<StatsLogEventWrapper> pull() = 0;
+ virtual vector<StatsLogEventWrapper> Pull() = 0;
};
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // STATSD_STATSPULLER_H
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 7f554d3e398e..f45cb1ccaa74 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -18,44 +18,58 @@
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
+#include <cutils/log.h>
+#include <algorithm>
+#include <climits>
#include "KernelWakelockPuller.h"
+#include "StatsPullerManager.h"
#include "StatsService.h"
-#include "external/StatsPullerManager.h"
#include "logd/LogEvent.h"
-#include <cutils/log.h>
-#include <algorithm>
#include <iostream>
-using namespace android;
+using std::string;
+using std::vector;
namespace android {
namespace os {
namespace statsd {
-const int StatsPullerManager::KERNEL_WAKELOCKS = 1;
+const int kernel_wakelock = 1;
+const unordered_map<string, int> StatsPullerManager::kPullCodes({{"KERNEL_WAKELOCK",
+ kernel_wakelock}});
-StatsPullerManager::StatsPullerManager() {
- mStatsPullers.insert(
- {static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
+StatsPullerManager::StatsPullerManager()
+ : mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
+ mPullers.insert({kernel_wakelock, make_unique<KernelWakelockPuller>()});
+ mStatsCompanionService = get_stats_companion_service();
+ if (mStatsCompanionService != nullptr) {
+ mStatsCompanionService->cancelPullingAlarms();
+ } else {
+ VLOG("Failed to update pulling interval");
+ }
}
-vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) {
+static const int log_msg_header_size = 28;
+
+vector<shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode, uint64_t timestampSec) {
if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
- vector<std::shared_ptr<LogEvent>> ret;
- if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
- vector<StatsLogEventWrapper> outputs = (mStatsPullers.find(pullCode)->second)->pull();
+ vector<shared_ptr<LogEvent>> ret;
+ auto itr = mPullers.find(pullCode);
+ if (itr != mPullers.end()) {
+ vector<StatsLogEventWrapper> outputs = itr->second->Pull();
for (const StatsLogEventWrapper& it : outputs) {
log_msg tmp;
+ tmp.entry_v1.sec = timestampSec;
+ tmp.entry_v1.nsec = 0;
tmp.entry_v1.len = it.bytes.size();
// Manually set the header size to 28 bytes to match the pushed log events.
- tmp.entry.hdr_size = 28;
+ tmp.entry.hdr_size = log_msg_header_size;
// And set the received bytes starting after the 28 bytes reserved for header.
- std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + 28);
- std::shared_ptr<LogEvent> evt = std::make_shared<LogEvent>(tmp);
+ copy(it.bytes.begin(), it.bytes.end(), tmp.buf + log_msg_header_size);
+ shared_ptr<LogEvent> evt = make_shared<LogEvent>(tmp);
ret.push_back(evt);
- // ret.emplace_back(tmp);
}
return ret;
} else {
@@ -64,6 +78,112 @@ vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) {
}
}
+sp<IStatsCompanionService> StatsPullerManager::get_stats_companion_service() {
+ sp<IStatsCompanionService> statsCompanion = nullptr;
+ // Get statscompanion service from service manager
+ const sp<IServiceManager> sm(defaultServiceManager());
+ if (sm != nullptr) {
+ const String16 name("statscompanion");
+ statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
+ if (statsCompanion == nullptr) {
+ ALOGW("statscompanion service unavailable!");
+ return nullptr;
+ }
+ }
+ return statsCompanion;
+}
+
+StatsPullerManager& StatsPullerManager::GetInstance() {
+ static StatsPullerManager instance;
+ return instance;
+}
+
+int StatsPullerManager::GetPullCode(string atomName) {
+ if (kPullCodes.find(atomName) != kPullCodes.end()) {
+ return kPullCodes.find(atomName)->second;
+ } else {
+ return -1;
+ }
+}
+
+long StatsPullerManager::get_pull_start_time_ms() {
+ // TODO: limit and align pull intervals to 10min boundaries if this turns out to be a problem
+ return time(nullptr) * 1000;
+}
+
+void StatsPullerManager::RegisterReceiver(int pullCode, sp<PullDataReceiver> receiver, long intervalMs) {
+ AutoMutex _l(mReceiversLock);
+ vector<ReceiverInfo>& receivers = mReceivers[pullCode];
+ for (auto it = receivers.begin(); it != receivers.end(); it++) {
+ if (it->receiver.get() == receiver.get()) {
+ VLOG("Receiver already registered of %d", (int)receivers.size());
+ return;
+ }
+ }
+ ReceiverInfo receiverInfo;
+ receiverInfo.receiver = receiver;
+ receiverInfo.timeInfo.first = intervalMs;
+ receivers.push_back(receiverInfo);
+
+ // There is only one alarm for all pulled events. So only set it to the smallest denom.
+ if (intervalMs < mCurrentPullingInterval) {
+ VLOG("Updating pulling interval %ld", intervalMs);
+ mCurrentPullingInterval = intervalMs;
+ if (mStatsCompanionService != nullptr) {
+ mStatsCompanionService->setPullingAlarms(mPullStartTimeMs, mCurrentPullingInterval);
+ } else {
+ VLOG("Failed to update pulling interval");
+ }
+ }
+ VLOG("Puller for pullcode %d registered of %d", pullCode, (int)receivers.size());
+}
+
+void StatsPullerManager::UnRegisterReceiver(int pullCode, sp<PullDataReceiver> receiver) {
+ AutoMutex _l(mReceiversLock);
+ if (mReceivers.find(pullCode) == mReceivers.end()) {
+ VLOG("Unknown pull code or no receivers: %d", pullCode);
+ return;
+ }
+ auto& receivers = mReceivers.find(pullCode)->second;
+ for (auto it = receivers.begin(); it != receivers.end(); it++) {
+ if (receiver.get() == it->receiver.get()) {
+ receivers.erase(it);
+ VLOG("Puller for pullcode %d unregistered of %d", pullCode, (int)receivers.size());
+ return;
+ }
+ }
+}
+
+void StatsPullerManager::OnAlarmFired() {
+ AutoMutex _l(mReceiversLock);
+
+ uint64_t currentTimeMs = time(nullptr) * 1000;
+
+ vector<pair<int, vector<ReceiverInfo*>>> needToPull =
+ vector<pair<int, vector<ReceiverInfo*>>>();
+ for (auto& pair : mReceivers) {
+ vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
+ if (pair.second.size() != 0){
+ for(auto& receiverInfo : pair.second) {
+ if (receiverInfo.timeInfo.first + receiverInfo.timeInfo.second > currentTimeMs) {
+ receivers.push_back(&receiverInfo);
+ }
+ }
+ if (receivers.size() > 0) {
+ needToPull.push_back(make_pair(pair.first, receivers));
+ }
+ }
+ }
+
+ for (const auto& pullInfo : needToPull) {
+ const vector<shared_ptr<LogEvent>>& data = Pull(pullInfo.first, currentTimeMs/1000);
+ for(const auto& receiverInfo : pullInfo.second) {
+ receiverInfo->receiver->onDataPulled(data);
+ receiverInfo->timeInfo.second = currentTimeMs;
+ }
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index e46aec1c060b..e599b6904ed4 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -14,38 +14,79 @@
* limitations under the License.
*/
-#ifndef STATSD_STATSPULLERMANAGER_H
-#define STATSD_STATSPULLERMANAGER_H
+#pragma once
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
+#include <utils/RefBase.h>
#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+#include <string>
#include <unordered_map>
-#include "external/StatsPuller.h"
+#include <vector>
+#include "PullDataReceiver.h"
+#include "StatsPuller.h"
#include "logd/LogEvent.h"
-#include "matchers/matcher_util.h"
namespace android {
namespace os {
namespace statsd {
-const static int KERNEL_WAKELOCKS = 1;
-
class StatsPullerManager : public virtual RefBase {
public:
- // Enums of pulled data types (pullCodes)
- // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
- // TODO: pull the constant from stats_events.proto instead
- const static int KERNEL_WAKELOCKS;
- StatsPullerManager();
+ static StatsPullerManager& GetInstance();
+
+ void RegisterReceiver(int pullCode, sp<PullDataReceiver> receiver, long intervalMs);
+
+ void UnRegisterReceiver(int pullCode, sp<PullDataReceiver> receiver);
// We return a vector of shared_ptr since LogEvent's copy constructor is not available.
- vector<std::shared_ptr<LogEvent>> Pull(const int pullCode);
+ vector<std::shared_ptr<LogEvent>> Pull(const int pullCode, const uint64_t timestampSec);
+
+ // Translate metric name to pullCodes.
+ // return -1 if no valid pullCode is found
+ int GetPullCode(std::string metricName);
+
+ void OnAlarmFired();
private:
- std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
+ StatsPullerManager();
+
+ sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+ sp<IStatsCompanionService> get_stats_companion_service();
+
+ std::unordered_map<int, std::unique_ptr<StatsPuller>> mPullers;
+
+
+
+ // internal state of a bucket.
+ typedef struct {
+ // pull_interval_sec : last_pull_time_sec
+ std::pair<uint64_t, uint64_t> timeInfo;
+ sp<PullDataReceiver> receiver;
+ } ReceiverInfo;
+
+ std::map<int, std::vector<ReceiverInfo>> mReceivers;
+
+ Mutex mReceiversLock;
+
+ long mCurrentPullingInterval;
+
+ // for value metrics, it is important for the buckets to be aligned to multiple of smallest
+ // bucket size. All pulled metrics start pulling based on this time, so that they can be
+ // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
+ // request time.
+ const long mPullStartTimeMs;
+
+ long get_pull_start_time_ms();
+
+ LogEvent parse_pulled_data(String16 data);
+
+ static const std::unordered_map<std::string, int> kPullCodes;
};
} // namespace statsd
} // namespace os
-} // namespace android
-
-#endif // STATSD_STATSPULLERMANAGER_H
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1a039f6d61c5..26a82cbf84bc 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define DEBUG true // STOPSHIP if true
#include "logd/LogEvent.h"
#include <sstream>
@@ -29,7 +30,7 @@ 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.
-LogEvent::LogEvent(log_msg msg) : mList(msg) {
+LogEvent::LogEvent(log_msg& msg) : mList(msg) {
init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
}
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 9ef20ea6968c..df75d9f2e0b1 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -42,7 +42,7 @@ public:
/**
* Read a LogEvent from a log_msg.
*/
- explicit LogEvent(log_msg msg);
+ explicit LogEvent(log_msg& msg);
/**
* Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 7bb9c8a502c1..9f8558d59baf 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,7 +21,6 @@
#include "CountMetricProducer.h"
#include "stats_util.h"
-#include <cutils/log.h>
#include <limits.h>
#include <stdlib.h>
@@ -94,7 +93,7 @@ static void addSlicedCounterToReport(StatsLogReport_CountMetricDataWrapper& wrap
}
}
-void CountMetricProducer::onSlicedConditionMayChange() {
+void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
}
@@ -128,7 +127,7 @@ StatsLogReport CountMetricProducer::onDumpReport() {
// TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
}
-void CountMetricProducer::onConditionChanged(const bool conditionMet) {
+void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
mCondition = conditionMet;
}
@@ -136,7 +135,7 @@ void CountMetricProducer::onConditionChanged(const bool conditionMet) {
void CountMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event) {
+ const LogEvent& event, bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
flushCounterIfNeeded(eventTimeNs);
@@ -203,6 +202,13 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
(long long)mCurrentBucketStartTimeNs);
}
+size_t CountMetricProducer::byteSize() {
+// TODO: return actual proto size when ProtoOutputStream is ready for use for
+// CountMetricsProducer.
+// return mProto->size();
+ return 0;
+}
+
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 340c8309b7fa..80e80d95d801 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -41,21 +41,26 @@ public:
virtual ~CountMetricProducer();
- void onConditionChanged(const bool conditionMet) override;
+ void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
StatsLogReport onDumpReport() override;
- void onSlicedConditionMayChange() override;
+ void onSlicedConditionMayChange(const uint64_t eventTime) override;
+
+ size_t byteSize() override;
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
protected:
void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey,
- bool condition, const LogEvent& event) override;
+ bool condition, const LogEvent& event,
+ bool scheduledPull) override;
private:
const CountMetric mMetric;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 38e55fde40d5..340f503fd651 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -15,11 +15,11 @@
*/
#define DEBUG true
-#include "DurationMetricProducer.h"
+
#include "Log.h"
+#include "DurationMetricProducer.h"
#include "stats_util.h"
-#include <cutils/log.h>
#include <limits.h>
#include <stdlib.h>
@@ -34,13 +34,15 @@ namespace statsd {
DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex,
- const sp<ConditionWizard>& wizard)
+ const sp<ConditionWizard>& wizard,
+ const vector<KeyMatcher>& internalDimension)
// 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),
mStartIndex(startIndex),
mStopIndex(stopIndex),
- mStopAllIndex(stopAllIndex) {
+ mStopAllIndex(stopAllIndex),
+ mInternalDimension(internalDimension) {
// TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
// them in the base class, because the proto generated CountMetric, and DurationMetric are
// not related. Maybe we should add a template in the future??
@@ -67,34 +69,43 @@ DurationMetricProducer::~DurationMetricProducer() {
VLOG("~DurationMetric() called");
}
+unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
+ vector<DurationBucketInfo>& bucket) {
+ switch (mMetric.type()) {
+ case DurationMetric_AggregationType_DURATION_SUM:
+ return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
+ mCurrentBucketStartTimeNs, mBucketSizeNs,
+ bucket);
+ case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
+ return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
+ mCurrentBucketStartTimeNs, mBucketSizeNs,
+ bucket);
+ }
+}
+
void DurationMetricProducer::finish() {
// TODO: write the StatsLogReport to dropbox using
// DropboxWriter.
}
-void DurationMetricProducer::onSlicedConditionMayChange() {
+void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
// Now for each of the on-going event, check if the condition has changed for them.
+ flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
- VLOG("Metric %lld current %s state: %d", mMetric.metric_id(), pair.first.c_str(),
- pair.second.state);
- if (pair.second.state == kStopped) {
- continue;
- }
- bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
- ConditionState::kTrue;
- VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
- noteConditionChanged(pair.first, conditionMet, time(nullptr) * 1000000000);
+ pair.second->onSlicedConditionMayChange(eventTime);
}
}
-void DurationMetricProducer::onConditionChanged(const bool conditionMet) {
+void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
mCondition = conditionMet;
// TODO: need to populate the condition change time from the event which triggers the condition
// change, instead of using current time.
+
+ flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
- noteConditionChanged(pair.first, conditionMet, time(nullptr) * 1000000000);
+ pair.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -107,7 +118,7 @@ static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper&
}
for (const auto& bucket : buckets) {
data->add_bucket_info()->CopyFrom(bucket);
- VLOG("\t bucket [%lld - %lld] count: %lld", bucket.start_bucket_nanos(),
+ VLOG("\t bucket [%lld - %lld] duration(ns): %lld", bucket.start_bucket_nanos(),
bucket.end_bucket_nanos(), bucket.duration_nanos());
}
}
@@ -120,7 +131,7 @@ StatsLogReport DurationMetricProducer::onDumpReport() {
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
- flushDurationIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
+ flushIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
report.set_end_report_nanos(mCurrentBucketStartTimeNs);
StatsLogReport_DurationMetricDataWrapper* wrapper = report.mutable_duration_metrics();
@@ -137,216 +148,57 @@ StatsLogReport DurationMetricProducer::onDumpReport() {
return report;
};
-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) {
- noteStopAll(event.GetTimestampNs());
+void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
+ if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
return;
}
- if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end() && mConditionSliced) {
- // add the durationInfo for the current bucket.
- auto& durationInfo = mCurrentSlicedDuration[eventKey];
- durationInfo.conditionKeys = conditionKeys;
+ VLOG("flushing...........");
+ for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) {
+ if (it->second->flushIfNeeded(eventTime)) {
+ VLOG("erase bucket for key %s", it->first.c_str());
+ mCurrentSlicedDuration.erase(it);
+ }
}
- if (matcherIndex == mStartIndex) {
- 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(), eventKey.c_str(),
- condition);
- noteStop(eventKey, event.GetTimestampNs());
- }
+ int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
}
-void DurationMetricProducer::noteConditionChanged(const HashableDimensionKey& key,
- const bool conditionMet,
- const uint64_t eventTime) {
- flushDurationIfNeeded(eventTime);
+void DurationMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKeys, bool condition,
+ const LogEvent& event, bool scheduledPull) {
+ flushIfNeeded(event.GetTimestampNs());
- auto it = mCurrentSlicedDuration.find(key);
- if (it == mCurrentSlicedDuration.end()) {
+ if (matcherIndex == mStopAllIndex) {
+ for (auto& pair : mCurrentSlicedDuration) {
+ pair.second->noteStopAll(event.GetTimestampNs());
+ }
return;
}
- switch (it->second.state) {
- case kStarted:
- // if condition becomes false, kStarted -> kPaused. Record the current duration.
- if (!conditionMet) {
- it->second.state = DurationState::kPaused;
- it->second.lastDuration =
- updateDuration(it->second.lastDuration,
- eventTime - it->second.lastStartTime, mMetric.type());
- VLOG("Metric %lld Key: %s Paused because condition is false ", mMetric.metric_id(),
- key.c_str());
- }
- break;
- case kStopped:
- // nothing to do if it's stopped.
- break;
- case kPaused:
- // if condition becomes true, kPaused -> kStarted. and the start time is the condition
- // change time.
- if (conditionMet) {
- it->second.state = DurationState::kStarted;
- it->second.lastStartTime = eventTime;
- VLOG("Metric %lld Key: %s Paused->Started", mMetric.metric_id(), key.c_str());
- }
- break;
- }
-}
+ HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
-void DurationMetricProducer::noteStart(const HashableDimensionKey& key, const bool conditionMet,
- const uint64_t eventTime) {
- // this will add an empty bucket for this key if it didn't exist before.
- DurationInfo& duration = mCurrentSlicedDuration[key];
-
- switch (duration.state) {
- case kStarted:
- // It's safe to do nothing here. even if condition is not true, it means we are about
- // to receive the condition change event.
- break;
- case kPaused:
- // Safe to do nothing here. kPaused is waiting for the condition change.
- break;
- case kStopped:
- if (!conditionMet) {
- // event started, but we need to wait for the condition to become true.
- duration.state = DurationState::kPaused;
- break;
- }
- duration.state = DurationState::kStarted;
- duration.lastStartTime = eventTime;
- break;
+ if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+ mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]);
}
-}
-void DurationMetricProducer::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
- if (mCurrentSlicedDuration.find(key) == mCurrentSlicedDuration.end()) {
- // we didn't see a start event before. do nothing.
- return;
- }
- DurationInfo& duration = mCurrentSlicedDuration[key];
-
- switch (duration.state) {
- case DurationState::kStopped:
- // already stopped, do nothing.
- break;
- case DurationState::kStarted: {
- duration.state = DurationState::kStopped;
- int64_t durationTime = eventTime - duration.lastStartTime;
- VLOG("Metric %lld, key %s, Stop %lld %lld %lld", mMetric.metric_id(), key.c_str(),
- (long long)duration.lastStartTime, (long long)eventTime, (long long)durationTime);
- duration.lastDuration =
- updateDuration(duration.lastDuration, durationTime, mMetric.type());
- VLOG(" record duration: %lld ", (long long)duration.lastDuration);
- break;
- }
- case DurationState::kPaused: {
- duration.state = DurationState::kStopped;
- break;
- }
- }
-}
+ auto it = mCurrentSlicedDuration.find(eventKey);
-int64_t DurationMetricProducer::updateDuration(const int64_t lastDuration,
- const int64_t durationTime,
- const DurationMetric_AggregationType type) {
- int64_t result = lastDuration;
- switch (type) {
- case DurationMetric_AggregationType_DURATION_SUM:
- result += durationTime;
- break;
- case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
- if (lastDuration < durationTime) {
- result = durationTime;
- }
- break;
- case DurationMetric_AggregationType_DURATION_MIN_SPARSE:
- if (lastDuration > durationTime) {
- result = durationTime;
- }
- break;
- }
- return result;
-}
+ if (matcherIndex == mStartIndex) {
+ it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
-void DurationMetricProducer::noteStopAll(const uint64_t eventTime) {
- for (auto& duration : mCurrentSlicedDuration) {
- noteStop(duration.first, eventTime);
+ } else if (matcherIndex == mStopIndex) {
+ it->second->noteStop(atomKey, event.GetTimestampNs());
}
}
-// When a new matched event comes in, we check if event falls into the current
-// bucket. If not, flush the old counter to past buckets and initialize the current buckt.
-void DurationMetricProducer::flushDurationIfNeeded(const uint64_t eventTime) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
- return;
- }
-
- // adjust the bucket start time
- int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
- DurationBucketInfo info;
- uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
- info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
- info.set_end_bucket_nanos(endTime);
-
- uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
- VLOG("Metric %lld: new bucket start time: %lld", mMetric.metric_id(),
- (long long)mCurrentBucketStartTimeNs);
-
- for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) {
- int64_t finalDuration = it->second.lastDuration;
- if (it->second.state == kStarted) {
- // the event is still on-going, duration needs to be updated.
- int64_t durationTime = endTime - it->second.lastStartTime;
- finalDuration = updateDuration(it->second.lastDuration, durationTime, mMetric.type());
- }
-
- VLOG(" final duration for last bucket: %lld", (long long)finalDuration);
-
- // Don't record empty bucket.
- if (finalDuration != 0) {
- info.set_duration_nanos(finalDuration);
- // it will auto create new vector of CountbucketInfo if the key is not found.
- auto& bucketList = mPastBuckets[it->first];
- bucketList.push_back(info);
- }
-
- // if the event is still on-going, add the buckets between previous bucket and now. Because
- // the event has been going on across all the buckets in between.
- // |prev_bucket|...|..|...|now_bucket|
- if (it->second.state == kStarted) {
- for (int i = 1; i < numBucketsForward; i++) {
- DurationBucketInfo info;
- info.set_start_bucket_nanos(oldBucketStartTimeNs + mBucketSizeNs * i);
- info.set_end_bucket_nanos(endTime + mBucketSizeNs * i);
- info.set_duration_nanos(mBucketSizeNs);
- auto& bucketList = mPastBuckets[it->first];
- bucketList.push_back(info);
- VLOG(" add filling bucket with duration %lld", (long long)mBucketSizeNs);
- }
- }
-
- if (it->second.state == DurationState::kStopped) {
- // No need to keep buckets for events that were stopped before. If the event starts
- // again, we will add it back.
- mCurrentSlicedDuration.erase(it);
- } else {
- // for kPaused, and kStarted event, we will keep the buckets, and reset the start time
- // and duration.
- it->second.lastStartTime = mCurrentBucketStartTimeNs;
- it->second.lastDuration = 0;
- }
- }
+size_t DurationMetricProducer::byteSize() {
+// TODO: return actual proto size when ProtoOutputStream is ready for use for
+// DurationMetricsProducer.
+// return mProto->size();
+ return 0;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 19e2437ca538..febf25d45cc2 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -22,6 +22,9 @@
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
+#include "duration_helper/DurationTracker.h"
+#include "duration_helper/MaxDurationTracker.h"
+#include "duration_helper/OringDurationTracker.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"
@@ -32,50 +35,35 @@ namespace android {
namespace os {
namespace statsd {
-enum DurationState {
- kStopped = 0, // The event is stopped.
- kStarted = 1, // The event is on going.
- kPaused = 2, // The event is started, but condition is false, clock is paused. When condition
- // turns to true, kPaused will become kStarted.
-};
-
-// Hold duration information for current on-going bucket.
-struct DurationInfo {
- DurationState state;
- // most recent start time.
- int64_t lastStartTime;
- // existing duration in current bucket. Eventually, the duration will be aggregated in
- // the way specified in AggregateType (Sum, Max, or Min).
- int64_t lastDuration;
- // cache the HashableDimensionKeys we need to query the condition for this duration event.
- std::map<string, HashableDimensionKey> conditionKeys;
-
- DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
-};
-
class DurationMetricProducer : public MetricProducer {
public:
DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
const size_t startIndex, const size_t stopIndex,
- const size_t stopAllIndex, const sp<ConditionWizard>& wizard);
+ const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
+ const vector<KeyMatcher>& internalDimension);
virtual ~DurationMetricProducer();
- void onConditionChanged(const bool conditionMet) override;
+ void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
StatsLogReport onDumpReport() override;
- void onSlicedConditionMayChange() override;
+ void onSlicedConditionMayChange(const uint64_t eventTime) override;
+
+ size_t byteSize() override;
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
protected:
void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKeys,
- bool condition, const LogEvent& event) override;
+ bool condition, const LogEvent& event,
+ bool scheduledPull) override;
private:
const DurationMetric mMetric;
@@ -89,26 +77,21 @@ private:
// Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
const size_t mStopAllIndex;
+ // The dimension from the atom predicate. e.g., uid, wakelock name.
+ const vector<KeyMatcher> mInternalDimension;
+
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<HashableDimensionKey, std::vector<DurationBucketInfo>> mPastBuckets;
// The current bucket.
- std::unordered_map<HashableDimensionKey, DurationInfo> mCurrentSlicedDuration;
+ std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
+ mCurrentSlicedDuration;
void flushDurationIfNeeded(const uint64_t newEventTime);
- void noteStart(const HashableDimensionKey& key, const bool conditionMet,
- const uint64_t eventTime);
-
- void noteStop(const HashableDimensionKey& key, const uint64_t eventTime);
-
- void noteStopAll(const uint64_t eventTime);
-
- static int64_t updateDuration(const int64_t lastDuration, const int64_t durationTime,
- const DurationMetric_AggregationType type);
+ std::unique_ptr<DurationTracker> createDurationTracker(std::vector<DurationBucketInfo>& bucket);
- void noteConditionChanged(const HashableDimensionKey& key, const bool conditionMet,
- const uint64_t eventTime);
+ void flushIfNeeded(uint64_t timestamp);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 8b3f405eec80..d71417936bfd 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -20,7 +20,6 @@
#include "EventMetricProducer.h"
#include "stats_util.h"
-#include <cutils/log.h>
#include <limits.h>
#include <stdlib.h>
@@ -78,7 +77,7 @@ void EventMetricProducer::startNewProtoOutputStream(long long startTime) {
void EventMetricProducer::finish() {
}
-void EventMetricProducer::onSlicedConditionMayChange() {
+void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
}
StatsLogReport EventMetricProducer::onDumpReport() {
@@ -105,7 +104,7 @@ StatsLogReport EventMetricProducer::onDumpReport() {
return StatsLogReport();
}
-void EventMetricProducer::onConditionChanged(const bool conditionMet) {
+void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
mCondition = conditionMet;
}
@@ -113,7 +112,7 @@ void EventMetricProducer::onConditionChanged(const bool conditionMet) {
void EventMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event) {
+ const LogEvent& event, bool scheduledPull) {
if (!condition) {
return;
@@ -127,6 +126,10 @@ void EventMetricProducer::onMatchedLogEventInternal(
mProto->end(wrapperToken);
}
+size_t EventMetricProducer::byteSize() {
+ return mProto->size();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 879175cc25ff..7dd0e38a0533 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -41,18 +41,22 @@ public:
void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey,
- bool condition, const LogEvent& event) override;
+ bool condition, const LogEvent& event, bool scheduledPull) override;
- void onConditionChanged(const bool conditionMet) override;
+ void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
StatsLogReport onDumpReport() override;
- void onSlicedConditionMayChange() override;
+ void onSlicedConditionMayChange(const uint64_t eventTime) override;
+
+ size_t byteSize() override;
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
private:
const EventMetric mMetric;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 3c8ce6e7dd1c..535f4a276fe1 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -21,7 +21,8 @@ namespace statsd {
using std::map;
-void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event,
+ bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
// this is old event, maybe statsd restarted?
if (eventTimeNs < mStartTimeNs) {
@@ -59,7 +60,8 @@ void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent
condition = mCondition;
}
- onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event);
+ onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event,
+ scheduledPull);
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 496b1453e46c..3b117ec4e307 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -48,11 +48,11 @@ public:
virtual ~MetricProducer(){};
// Consume the parsed stats log entry that already matched the "what" of the metric.
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event);
+ void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event, bool scheduledPull);
- virtual void onConditionChanged(const bool condition) = 0;
+ virtual void onConditionChanged(const bool condition, const uint64_t eventTime) = 0;
- virtual void onSlicedConditionMayChange() = 0;
+ virtual void onSlicedConditionMayChange(const uint64_t eventTime) = 0;
// This is called when the metric collecting is done, e.g., when there is a new configuration
// coming. MetricProducer should do the clean up, and dump existing data to dropbox.
@@ -64,6 +64,8 @@ public:
return mConditionSliced;
};
+ virtual size_t byteSize() = 0;
+
protected:
const uint64_t mStartTimeNs;
@@ -105,7 +107,7 @@ protected:
virtual void onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event) = 0;
+ const LogEvent& event, bool scheduledPull) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 1ffa58b8c862..521fcc33ac47 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -74,6 +74,7 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
}
int tagId = event.GetTagId();
+ uint64_t eventTime = event.GetTimestampNs();
if (mTagIds.find(tagId) == mTagIds.end()) {
// not interesting...
return;
@@ -124,13 +125,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
// metric cares about non sliced condition, and it's changed.
// Push the new condition to it directly.
if (!mAllMetricProducers[metricIndex]->isConditionSliced() && changedCache[i]) {
- mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i]);
+ mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
+ eventTime);
// metric cares about sliced conditions, and it may have changed. Send
// notification, and the metric can query the sliced conditions that are
// interesting to it.
} else if (mAllMetricProducers[metricIndex]->isConditionSliced() &&
slicedChangedCache[i]) {
- mAllMetricProducers[metricIndex]->onSlicedConditionMayChange();
+ mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(eventTime);
}
}
}
@@ -143,13 +145,23 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
if (pair != mTrackerToMetricMap.end()) {
auto& metricList = pair->second;
for (const int metricIndex : metricList) {
- mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
+ // pushed metrics are never scheduled pulls
+ mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event, false);
}
}
}
}
}
+// Returns the total byte size of all metrics managed by a single config source.
+size_t MetricsManager::byteSize() {
+ size_t totalSize = 0;
+ for (auto metricProducer : mAllMetricProducers) {
+ totalSize += metricProducer->byteSize();
+ }
+ return totalSize;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 2f91460061fa..44cd6371199e 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -46,6 +46,8 @@ public:
// Config source owner can call onDumpReport() to get all the metrics collected.
std::vector<StatsLogReport> onDumpReport();
+ size_t byteSize();
+
private:
// All event tags that are interesting to my metrics.
std::set<int> mTagIds;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
new file mode 100644
index 000000000000..cb6166dafa3a
--- /dev/null
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "ValueMetricProducer.h"
+
+#include <cutils/log.h>
+#include <limits.h>
+#include <stdlib.h>
+
+using std::map;
+using std::unordered_map;
+using std::list;
+using std::make_shared;
+using std::shared_ptr;
+using std::unique_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
+ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard)
+ : MetricProducer((time(nullptr) / 600 * 600 * NANO_SECONDS_IN_A_SECOND), conditionIndex,
+ wizard),
+ mMetric(metric),
+ mPullCode(mStatsPullerManager.GetPullCode(mMetric.what())) {
+ // TODO: valuemetric for pushed events may need unlimited bucket length
+ mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+
+ mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+
+ if (metric.links().size() > 0) {
+ mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
+ metric.links().end());
+ mConditionSliced = true;
+ }
+
+ if (!metric.has_condition() && mPullCode != -1) {
+ mStatsPullerManager.RegisterReceiver(mPullCode, this, metric.bucket().bucket_size_millis());
+ }
+
+ VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ (long long)mBucketSizeNs, (long long)mStartTimeNs);
+}
+
+ValueMetricProducer::~ValueMetricProducer() {
+ VLOG("~ValueMetricProducer() called");
+}
+
+void ValueMetricProducer::finish() {
+ // TODO: write the StatsLogReport to dropbox using
+ // DropboxWriter.
+}
+
+static void addSlicedCounterToReport(StatsLogReport_ValueMetricDataWrapper& wrapper,
+ const vector<KeyValuePair>& key,
+ const vector<ValueBucketInfo>& buckets) {
+ ValueMetricData* data = wrapper.add_data();
+ for (const auto& kv : key) {
+ data->add_dimension()->CopyFrom(kv);
+ }
+ for (const auto& bucket : buckets) {
+ data->add_bucket_info()->CopyFrom(bucket);
+ VLOG("\t bucket [%lld - %lld] value: %lld", bucket.start_bucket_nanos(),
+ bucket.end_bucket_nanos(), bucket.value());
+ }
+}
+
+void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
+ VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+}
+
+StatsLogReport ValueMetricProducer::onDumpReport() {
+ VLOG("metric %lld dump report now...", mMetric.metric_id());
+
+ StatsLogReport report;
+ report.set_metric_id(mMetric.metric_id());
+ report.set_start_report_nanos(mStartTimeNs);
+
+ // Dump current bucket if it's stale.
+ // If current bucket is still on-going, don't force dump current bucket.
+ // In finish(), We can force dump current bucket.
+ // flush_if_needed(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
+ report.set_end_report_nanos(mCurrentBucketStartTimeNs);
+
+ StatsLogReport_ValueMetricDataWrapper* wrapper = report.mutable_value_metrics();
+
+ for (const auto& pair : mPastBuckets) {
+ const HashableDimensionKey& hashableKey = pair.first;
+ auto it = mDimensionKeyMap.find(hashableKey);
+ if (it == mDimensionKeyMap.end()) {
+ ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
+ continue;
+ }
+
+ VLOG(" dimension key %s", hashableKey.c_str());
+ addSlicedCounterToReport(*wrapper, it->second, pair.second);
+ }
+ return report;
+ // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
+}
+
+void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
+ mCondition = condition;
+
+ if (mPullCode != -1) {
+ vector<shared_ptr<LogEvent>> allData = mStatsPullerManager.Pull(mPullCode, eventTime);
+ if (mCondition == true) {
+ mStatsPullerManager.RegisterReceiver(mPullCode, this,
+ mMetric.bucket().bucket_size_millis());
+ } else if (mCondition == ConditionState::kFalse) {
+ mStatsPullerManager.UnRegisterReceiver(mPullCode, this);
+ }
+ if (allData.size() == 0) {
+ return;
+ }
+ AutoMutex _l(mLock);
+ if (allData.size() == 0) {
+ return;
+ }
+ for (const auto& data : allData) {
+ onMatchedLogEvent(0, *data, false);
+ }
+ flush_if_needed(eventTime);
+ }
+ return;
+}
+
+void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
+ if (mCondition == ConditionState::kTrue || !mMetric.has_condition()) {
+ AutoMutex _l(mLock);
+ if (allData.size() == 0) {
+ return;
+ }
+ uint64_t eventTime = allData.at(0)->GetTimestampNs();
+ for (const auto& data : allData) {
+ onMatchedLogEvent(0, *data, true);
+ }
+ flush_if_needed(eventTime);
+ }
+}
+
+void ValueMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event, bool scheduledPull) {
+ uint64_t eventTimeNs = event.GetTimestampNs();
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+ (long long)mCurrentBucketStartTimeNs);
+ return;
+ }
+
+ Interval& interval = mCurrentSlicedBucket[eventKey];
+
+ long value = get_value(event);
+
+ if (scheduledPull) {
+ if (interval.raw.size() > 0) {
+ interval.raw.back().second = value;
+ } else {
+ interval.raw.push_back(std::make_pair(value, value));
+ }
+ mNextSlicedBucket[eventKey].raw[0].first = value;
+ } else {
+ if (mCondition == ConditionState::kTrue) {
+ interval.raw.push_back(std::make_pair(value, 0));
+ } else {
+ if (interval.raw.size() != 0) {
+ interval.raw.back().second = value;
+ }
+ }
+ }
+ if (mPullCode == -1) {
+ flush_if_needed(eventTimeNs);
+ }
+}
+
+long ValueMetricProducer::get_value(const LogEvent& event) {
+ status_t err = NO_ERROR;
+ long val = event.GetLong(mMetric.value_field(), &err);
+ if (err == NO_ERROR) {
+ return val;
+ } else {
+ VLOG("Can't find value in message.");
+ return 0;
+ }
+}
+
+void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
+ if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
+ VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
+ (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
+ return;
+ }
+
+ VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
+ (int)mCurrentSlicedBucket.size());
+ ValueBucketInfo info;
+ info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
+ info.set_end_bucket_nanos(mCurrentBucketStartTimeNs + mBucketSizeNs);
+
+ for (const auto& slice : mCurrentSlicedBucket) {
+ long value = 0;
+ for (const auto& pair : slice.second.raw) {
+ value += pair.second - pair.first;
+ }
+ info.set_value(value);
+ VLOG(" %s, %ld", slice.first.c_str(), value);
+ // it will auto create new vector of ValuebucketInfo if the key is not found.
+ auto& bucketList = mPastBuckets[slice.first];
+ bucketList.push_back(info);
+ }
+
+ // Reset counters
+ mCurrentSlicedBucket.swap(mNextSlicedBucket);
+ mNextSlicedBucket.clear();
+ int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ if (numBucketsForward >1) {
+ VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
+ }
+ mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+ VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ (long long)mCurrentBucketStartTimeNs);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
new file mode 100644
index 000000000000..4f1791351f87
--- /dev/null
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/threads.h>
+#include <list>
+#include "../condition/ConditionTracker.h"
+#include "../external/PullDataReceiver.h"
+#include "../external/StatsPullerManager.h"
+#include "CountAnomalyTracker.h"
+#include "MetricProducer.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+public:
+ ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard);
+
+ virtual ~ValueMetricProducer();
+
+ void onConditionChanged(const bool condition, const uint64_t eventTime) override;
+
+ void finish() override;
+
+ StatsLogReport onDumpReport() override;
+
+ void onSlicedConditionMayChange(const uint64_t eventTime);
+
+ void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
+ // TODO: Implement this later.
+ size_t byteSize() override{return 0;};
+
+ // TODO: Implement this later.
+ virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
+
+protected:
+ void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey,
+ bool condition, const LogEvent& event,
+ bool scheduledPull) override;
+
+private:
+ const ValueMetric mMetric;
+
+ StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+
+ Mutex mLock;
+
+ const int mPullCode;
+
+ // internal state of a bucket.
+ typedef struct {
+ std::vector<std::pair<long, long>> raw;
+ } Interval;
+
+ std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
+ // If condition is true and pulling on schedule, the previous bucket value needs to be carried
+ // over to the next bucket.
+ std::unordered_map<HashableDimensionKey, Interval> mNextSlicedBucket;
+
+ // Save the past buckets and we can clear when the StatsLogReport is dumped.
+ std::unordered_map<HashableDimensionKey, std::vector<ValueBucketInfo>> mPastBuckets;
+
+ long get_value(const LogEvent& event);
+
+ void flush_if_needed(const uint64_t eventTimeNs);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
new file mode 100644
index 000000000000..0d0d9a4e2647
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DURATION_TRACKER_H
+#define DURATION_TRACKER_H
+
+#include "condition/ConditionWizard.h"
+#include "stats_util.h"
+
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+enum DurationState {
+ kStopped = 0, // The event is stopped.
+ kStarted = 1, // The event is on going.
+ kPaused = 2, // The event is started, but condition is false, clock is paused. When condition
+ // turns to true, kPaused will become kStarted.
+};
+
+// Hold duration information for one atom level duration in current on-going bucket.
+struct DurationInfo {
+ DurationState state;
+ // most recent start time.
+ int64_t lastStartTime;
+ // existing duration in current bucket.
+ int64_t lastDuration;
+ // TODO: Optimize the way we track sliced condition in duration metrics.
+ // cache the HashableDimensionKeys we need to query the condition for this duration event.
+ ConditionKey conditionKeys;
+
+ DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
+};
+
+class DurationTracker {
+public:
+ DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs, std::vector<DurationBucketInfo>& bucket)
+ : mWizard(wizard),
+ mConditionTrackerIndex(conditionIndex),
+ mCurrentBucketStartTimeNs(currentBucketStartNs),
+ mBucketSizeNs(bucketSizeNs),
+ mBucket(bucket),
+ mDuration(0){};
+ virtual ~DurationTracker(){};
+ virtual void noteStart(const HashableDimensionKey& key, bool condition,
+ const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
+ virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) = 0;
+ virtual void noteStopAll(const uint64_t eventTime) = 0;
+ virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0;
+ virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0;
+ // Flush stale buckets if needed, and return true if the tracker has no on-going duration
+ // events, so that the owner can safely remove the tracker.
+ virtual bool flushIfNeeded(uint64_t timestampNs) = 0;
+
+protected:
+ sp<ConditionWizard> mWizard;
+
+ int mConditionTrackerIndex;
+
+ uint64_t mCurrentBucketStartTimeNs;
+
+ int64_t mBucketSizeNs;
+
+ std::vector<DurationBucketInfo>& mBucket; // where to write output
+
+ int64_t mDuration; // current recorded duration result
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // DURATION_TRACKER_H \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
new file mode 100644
index 000000000000..856ca9d979f5
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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
+
+#include "Log.h"
+#include "MaxDurationTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket)
+ : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) {
+}
+
+void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
+ const uint64_t eventTime, const ConditionKey& conditionKey) {
+ // this will construct a new DurationInfo if this key didn't exist.
+ DurationInfo& duration = mInfos[key];
+ duration.conditionKeys = conditionKey;
+ VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition);
+
+ switch (duration.state) {
+ case kStarted:
+ // The same event is already started. Because we are not counting nesting, so ignore.
+ break;
+ case kPaused:
+ // Safe to do nothing here. Paused means started but condition is false.
+ break;
+ case kStopped:
+ if (!condition) {
+ // event started, but we need to wait for the condition to become true.
+ duration.state = DurationState::kPaused;
+ } else {
+ duration.state = DurationState::kStarted;
+ duration.lastStartTime = eventTime;
+ }
+ break;
+ }
+}
+
+void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
+ VLOG("MaxDuration: key %s stop", key.c_str());
+ if (mInfos.find(key) == mInfos.end()) {
+ // we didn't see a start event before. do nothing.
+ return;
+ }
+ DurationInfo& duration = mInfos[key];
+
+ switch (duration.state) {
+ case DurationState::kStopped:
+ // already stopped, do nothing.
+ break;
+ case DurationState::kStarted: {
+ duration.state = DurationState::kStopped;
+ int64_t durationTime = eventTime - duration.lastStartTime;
+ VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime,
+ (long long)eventTime, (long long)durationTime);
+ duration.lastDuration = duration.lastDuration + durationTime;
+ VLOG(" record duration: %lld ", (long long)duration.lastDuration);
+ break;
+ }
+ case DurationState::kPaused: {
+ duration.state = DurationState::kStopped;
+ break;
+ }
+ }
+
+ if (duration.lastDuration > mDuration) {
+ mDuration = duration.lastDuration;
+ VLOG("Max: new max duration: %lld", (long long)mDuration);
+ }
+ // Once an atom duration ends, we erase it. Next time, if we see another atom event with the
+ // same name, they are still considered as different atom durations.
+ mInfos.erase(key);
+}
+void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
+ for (auto& pair : mInfos) {
+ noteStop(pair.first, eventTime);
+ }
+}
+
+bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) {
+ if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+ return false;
+ }
+
+ VLOG("MaxDurationTracker flushing.....");
+
+ // adjust the bucket start time
+ int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+
+ DurationBucketInfo info;
+ uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
+ info.set_end_bucket_nanos(endTime);
+
+ uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
+
+ bool hasOnGoingStartedEvent = false; // a kStarted event last across bucket boundaries.
+ bool hasPendingEvent =
+ false; // has either a kStarted or kPaused event across bucket boundaries
+ // meaning we need to carry them over to the new bucket.
+ for (auto it = mInfos.begin(); it != mInfos.end(); ++it) {
+ int64_t finalDuration = it->second.lastDuration;
+ if (it->second.state == kStarted) {
+ // the event is still on-going, duration needs to be updated.
+ // |..lastDurationTime_recorded...last_start -----|bucket_end. We need to record the
+ // duration between lastStartTime and bucketEnd.
+ int64_t durationTime = endTime - it->second.lastStartTime;
+
+ finalDuration += durationTime;
+ VLOG(" unrecorded %lld -> %lld", (long long)(durationTime), (long long)finalDuration);
+ // if the event is still on-going, we need to fill the buckets between prev_bucket and
+ // now_bucket. |prev_bucket|...|..|...|now_bucket|
+ hasOnGoingStartedEvent = true;
+ }
+
+ if (finalDuration > mDuration) {
+ mDuration = finalDuration;
+ }
+
+ if (it->second.state == DurationState::kStopped) {
+ // No need to keep buckets for events that were stopped before.
+ mInfos.erase(it);
+ } else {
+ hasPendingEvent = true;
+ // for kPaused, and kStarted event, we will keep track of them, and reset the start time
+ // and duration.
+ it->second.lastStartTime = mCurrentBucketStartTimeNs;
+ it->second.lastDuration = 0;
+ }
+ }
+
+ if (mDuration != 0) {
+ info.set_duration_nanos(mDuration);
+ mBucket.push_back(info);
+ VLOG(" final duration for last bucket: %lld", (long long)mDuration);
+ }
+
+ mDuration = 0;
+ if (hasOnGoingStartedEvent) {
+ for (int i = 1; i < numBucketsForward; i++) {
+ DurationBucketInfo info;
+ info.set_start_bucket_nanos(oldBucketStartTimeNs + mBucketSizeNs * i);
+ info.set_end_bucket_nanos(endTime + mBucketSizeNs * i);
+ info.set_duration_nanos(mBucketSizeNs);
+ mBucket.push_back(info);
+ VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs);
+ }
+ }
+ // If this tracker has no pending events, tell owner to remove.
+ return !hasPendingEvent;
+}
+
+void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
+ // VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ // Now for each of the on-going event, check if the condition has changed for them.
+ for (auto& pair : mInfos) {
+ if (pair.second.state == kStopped) {
+ continue;
+ }
+ bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
+ ConditionState::kTrue;
+ VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
+ noteConditionChanged(pair.first, conditionMet, timestamp);
+ }
+}
+
+void MaxDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
+ for (auto& pair : mInfos) {
+ noteConditionChanged(pair.first, condition, timestamp);
+ }
+}
+
+void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
+ const uint64_t timestamp) {
+ auto it = mInfos.find(key);
+ if (it == mInfos.end()) {
+ return;
+ }
+
+ switch (it->second.state) {
+ case kStarted:
+ // if condition becomes false, kStarted -> kPaused. Record the current duration.
+ if (!conditionMet) {
+ it->second.state = DurationState::kPaused;
+ it->second.lastDuration += (timestamp - it->second.lastStartTime);
+
+ VLOG("MaxDurationTracker Key: %s Started->Paused ", key.c_str());
+ }
+ break;
+ case kStopped:
+ // nothing to do if it's stopped.
+ break;
+ case kPaused:
+ // if condition becomes true, kPaused -> kStarted. and the start time is the condition
+ // change time.
+ if (conditionMet) {
+ it->second.state = DurationState::kStarted;
+ it->second.lastStartTime = timestamp;
+ VLOG("MaxDurationTracker Key: %s Paused->Started", key.c_str());
+ }
+ break;
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
new file mode 100644
index 000000000000..c74d070a3297
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -0,0 +1,55 @@
+/*
+ * 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 MAX_DURATION_TRACKER_H
+#define MAX_DURATION_TRACKER_H
+
+#include "DurationTracker.h"
+
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Tracks a pool of atom durations, and output the max duration for each bucket.
+// To get max duration, we need to keep track of each individual durations, and compare them when
+// they stop or bucket expires.
+class MaxDurationTracker : public DurationTracker {
+public:
+ MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket);
+ void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
+ const ConditionKey& conditionKey) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStopAll(const uint64_t eventTime) override;
+ bool flushIfNeeded(uint64_t timestampNs) override;
+ void onSlicedConditionMayChange(const uint64_t timestamp) override;
+ void onConditionChanged(bool condition, const uint64_t timestamp) override;
+
+private:
+ std::map<HashableDimensionKey, DurationInfo> mInfos;
+
+ void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
+ const uint64_t timestamp);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // MAX_DURATION_TRACKER_H
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
new file mode 100644
index 000000000000..e045fb4fb012
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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
+#include "Log.h"
+#include "OringDurationTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket)
+ : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket),
+ mStarted(),
+ mPaused() {
+ mLastStartTime = 0;
+}
+
+void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
+ const uint64_t eventTime, const ConditionKey& conditionKey) {
+ if (condition) {
+ if (mStarted.size() == 0) {
+ mLastStartTime = eventTime;
+ VLOG("record first start....");
+ }
+ mStarted.insert(key);
+ } else {
+ mPaused.insert(key);
+ }
+
+ if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
+ mConditionKeyMap[key] = conditionKey;
+ }
+
+ VLOG("Oring: %s start, condition %d", key.c_str(), condition);
+}
+
+void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) {
+ VLOG("Oring: %s stop", key.c_str());
+ auto it = mStarted.find(key);
+ if (it != mStarted.end()) {
+ mStarted.erase(it);
+ if (mStarted.empty()) {
+ mDuration += (timestamp - mLastStartTime);
+ VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
+ (long long)mDuration);
+ }
+ }
+
+ mPaused.erase(key);
+ mConditionKeyMap.erase(key);
+}
+void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
+ if (!mStarted.empty()) {
+ mDuration += (timestamp - mLastStartTime);
+ VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
+ (long long)mDuration);
+ }
+
+ mStarted.clear();
+ mPaused.clear();
+ mConditionKeyMap.clear();
+}
+
+bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
+ if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+ return false;
+ }
+ VLOG("OringDurationTracker Flushing.............");
+ // adjust the bucket start time
+ int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ DurationBucketInfo info;
+ uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
+ info.set_end_bucket_nanos(endTime);
+
+ uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
+
+ if (mStarted.size() > 0) {
+ mDuration += (endTime - mLastStartTime);
+ }
+ if (mDuration != 0) {
+ info.set_duration_nanos(mDuration);
+ // it will auto create new vector of CountbucketInfo if the key is not found.
+ mBucket.push_back(info);
+ VLOG(" duration: %lld", (long long)mDuration);
+ }
+
+ if (mStarted.size() > 0) {
+ for (int i = 1; i < numBucketsForward; i++) {
+ DurationBucketInfo info;
+ info.set_start_bucket_nanos(oldBucketStartTimeNs + mBucketSizeNs * i);
+ info.set_end_bucket_nanos(endTime + mBucketSizeNs * i);
+ info.set_duration_nanos(mBucketSizeNs);
+ mBucket.push_back(info);
+ VLOG(" add filling bucket with duration %lld", (long long)mBucketSizeNs);
+ }
+ }
+ mLastStartTime = mCurrentBucketStartTimeNs;
+ mDuration = 0;
+
+ // if all stopped, then tell owner it's safe to remove this tracker.
+ return mStarted.empty() && mPaused.empty();
+}
+
+void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
+ vector<HashableDimensionKey> startedToPaused;
+ vector<HashableDimensionKey> pausedToStarted;
+ if (!mStarted.empty()) {
+ for (auto it = mStarted.begin(); it != mStarted.end();) {
+ auto key = *it;
+ if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
+ VLOG("Key %s dont have condition key", key.c_str());
+ ++it;
+ continue;
+ }
+ if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
+ ConditionState::kTrue) {
+ it = mStarted.erase(it);
+ startedToPaused.push_back(key);
+ VLOG("Key %s started -> paused", key.c_str());
+ } else {
+ ++it;
+ }
+ }
+
+ if (mStarted.empty()) {
+ mDuration += (timestamp - mLastStartTime);
+ VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
+ (long long)mDuration);
+ }
+ }
+
+ if (!mPaused.empty()) {
+ for (auto it = mPaused.begin(); it != mPaused.end();) {
+ auto key = *it;
+ if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
+ VLOG("Key %s dont have condition key", key.c_str());
+ ++it;
+ continue;
+ }
+ if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
+ ConditionState::kTrue) {
+ it = mPaused.erase(it);
+ pausedToStarted.push_back(key);
+ VLOG("Key %s paused -> started", key.c_str());
+ } else {
+ ++it;
+ }
+ }
+
+ if (mStarted.empty() && pausedToStarted.size() > 0) {
+ mLastStartTime = timestamp;
+ }
+ }
+
+ mStarted.insert(pausedToStarted.begin(), pausedToStarted.end());
+ mPaused.insert(startedToPaused.begin(), startedToPaused.end());
+}
+
+void OringDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
+ if (condition) {
+ if (!mPaused.empty()) {
+ VLOG("Condition true, all started");
+ if (mStarted.empty()) {
+ mLastStartTime = timestamp;
+ }
+ mStarted.insert(mPaused.begin(), mPaused.end());
+ }
+ } else {
+ if (!mStarted.empty()) {
+ VLOG("Condition false, all paused");
+ mDuration += (timestamp - mLastStartTime);
+ mPaused.insert(mStarted.begin(), mStarted.end());
+ }
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
new file mode 100644
index 000000000000..542525153fb0
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -0,0 +1,57 @@
+/*
+ * 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 ORING_DURATION_TRACKER_H
+#define ORING_DURATION_TRACKER_H
+
+#include "DurationTracker.h"
+
+#include <set>
+namespace android {
+namespace os {
+namespace statsd {
+
+// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
+class OringDurationTracker : public DurationTracker {
+public:
+ OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket);
+ void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
+ const ConditionKey& conditionKey) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStopAll(const uint64_t eventTime) override;
+ void onSlicedConditionMayChange(const uint64_t timestamp) override;
+ void onConditionChanged(bool condition, const uint64_t timestamp) override;
+ bool flushIfNeeded(uint64_t timestampNs) override;
+
+private:
+ // We don't need to keep track of individual durations. The information that's needed is:
+ // 1) which keys are started. We record the first start time.
+ // 2) which keys are paused (started but condition was false)
+ // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
+ // it means everything has stopped, we then record the end time.
+ std::set<HashableDimensionKey> mStarted;
+ std::set<HashableDimensionKey> mPaused;
+ int64_t mLastStartTime;
+ std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // ORING_DURATION_TRACKER_H \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index e90f998a7179..3d4036e8d176 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -16,11 +16,13 @@
#include "../condition/CombinationConditionTracker.h"
#include "../condition/SimpleConditionTracker.h"
+#include "../external/StatsPullerManager.h"
#include "../matchers/CombinationLogMatchingTracker.h"
#include "../matchers/SimpleLogMatchingTracker.h"
#include "CountMetricProducer.h"
#include "DurationMetricProducer.h"
#include "EventMetricProducer.h"
+#include "ValueMetricProducer.h"
#include "stats_util.h"
using std::set;
@@ -33,6 +35,8 @@ namespace os {
namespace statsd {
bool handleMetricWithLogTrackers(const string what, const int metricIndex,
+ const bool usedForDimension,
+ const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
const unordered_map<string, int>& logTrackerMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap,
int& logTrackerIndex) {
@@ -41,6 +45,12 @@ bool handleMetricWithLogTrackers(const string what, const int metricIndex,
ALOGW("cannot find the LogEntryMatcher %s in config", what.c_str());
return false;
}
+ if (usedForDimension && allLogEntryMatchers[logTrackerIt->second]->getTagIds().size() > 1) {
+ ALOGE("LogEntryMatcher %s has more than one tag ids. When a metric has dimension, the "
+ "\"what\" can only about one atom type.",
+ what.c_str());
+ return false;
+ }
logTrackerIndex = logTrackerIt->second;
auto& metric_list = trackerToMetricMap[logTrackerIndex];
metric_list.push_back(metricIndex);
@@ -68,8 +78,7 @@ bool handleMetricWithConditions(
}
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()));
+ // TODO: We need to verify the link is valid.
}
conditionIndex = condition_it->second;
@@ -176,6 +185,7 @@ bool initConditions(const StatsdConfig& config, const unordered_map<string, int>
bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
const unordered_map<string, int>& conditionTrackerMap,
+ const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
unordered_map<int, std::vector<int>>& conditionToMetricMap,
@@ -184,6 +194,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
const int allMetricsCount =
config.count_metric_size() + config.duration_metric_size() + config.event_metric_size();
allMetricProducers.reserve(allMetricsCount);
+ StatsPullerManager& statsPullerManager = StatsPullerManager::GetInstance();
// Build MetricProducers for each metric defined in config.
// (1) build CountMetricProducer
@@ -196,8 +207,9 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
int metricIndex = allMetricProducers.size();
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndex)) {
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+ allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
+ trackerIndex)) {
return false;
}
@@ -215,26 +227,49 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
for (int i = 0; i < config.duration_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const DurationMetric& metric = config.duration_metric(i);
+
+ auto what_it = conditionTrackerMap.find(metric.what());
+ if (what_it == conditionTrackerMap.end()) {
+ ALOGE("DurationMetric's \"what\" is invalid");
+ return false;
+ }
+
+ const Condition& durationWhat = config.condition(what_it->second);
+
+ if (durationWhat.contents_case() != Condition::ContentsCase::kSimpleCondition) {
+ ALOGE("DurationMetric's \"what\" must be a simple condition");
+ return false;
+ }
+
+ const auto& simpleCondition = durationWhat.simple_condition();
+
int trackerIndices[3] = {-1, -1, -1};
- if (!metric.has_start() ||
- !handleMetricWithLogTrackers(metric.start(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndices[0])) {
+ if (!simpleCondition.has_start() ||
+ !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
+ metric.dimension_size() > 0, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
ALOGE("Duration metrics must specify a valid the start event matcher");
return false;
}
- if (metric.has_stop() &&
- !handleMetricWithLogTrackers(metric.stop(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndices[1])) {
+ if (simpleCondition.has_stop() &&
+ !handleMetricWithLogTrackers(simpleCondition.stop(), metricIndex,
+ metric.dimension_size() > 0, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
return false;
}
- if (metric.has_stop_all() &&
- !handleMetricWithLogTrackers(metric.stop_all(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndices[2])) {
+ if (simpleCondition.has_stop_all() &&
+ !handleMetricWithLogTrackers(simpleCondition.stop_all(), metricIndex,
+ metric.dimension_size() > 0, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
return false;
}
+ vector<KeyMatcher> internalDimension;
+ internalDimension.insert(internalDimension.begin(), simpleCondition.dimension().begin(),
+ simpleCondition.dimension().end());
+
int conditionIndex = -1;
if (metric.has_predicate()) {
@@ -243,9 +278,9 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
conditionToMetricMap);
}
- sp<MetricProducer> durationMetric =
- new DurationMetricProducer(metric, conditionIndex, trackerIndices[0],
- trackerIndices[1], trackerIndices[2], wizard);
+ sp<MetricProducer> durationMetric = new DurationMetricProducer(
+ metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
+ wizard, internalDimension);
allMetricProducers.push_back(durationMetric);
}
@@ -258,8 +293,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
return false;
}
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndex)) {
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndex)) {
return false;
}
@@ -275,6 +310,34 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
allMetricProducers.push_back(eventMetric);
}
+ // value metrics
+ for (int i = 0; i < config.value_metric_size(); i++) {
+ const ValueMetric& metric = config.value_metric(i);
+ if (!metric.has_what()) {
+ ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+ return false;
+ }
+
+ int pullCode = statsPullerManager.GetPullCode(metric.what());
+ if (pullCode == -1) {
+ ALOGW("cannot find %s in pulled metrics", metric.what().c_str());
+ return false;
+ }
+
+ sp<MetricProducer> valueProducer;
+ 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;
+ }
+ int metricIndex = allMetricProducers.size();
+ valueProducer = new ValueMetricProducer(metric, condition_it->second, wizard);
+ // will create new vector if not exist before.
+ auto& metricList = conditionToMetricMap[condition_it->second];
+ metricList.push_back(metricIndex);
+
+ allMetricProducers.push_back(valueProducer);
+ }
return true;
}
@@ -299,8 +362,9 @@ bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
return false;
}
- if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap)) {
+ if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
+ allConditionTrackers, allMetricProducers, conditionToMetricMap,
+ trackerToMetricMap)) {
ALOGE("initMetricProducers failed");
return false;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 6722eb3cfe72..e089d0653238 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -21,6 +21,7 @@
#include <vector>
#include "../condition/ConditionTracker.h"
+#include "../external/StatsPullerManager.h"
#include "../matchers/LogMatchingTracker.h"
namespace android {
@@ -75,6 +76,7 @@ bool initMetrics(
const StatsdConfig& config, const std::unordered_map<std::string, int>& logTrackerMap,
const std::unordered_map<std::string, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
+ const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h
index 8b948dee887e..13e776fb1f80 100644
--- a/cmds/statsd/src/packages/PackageInfoListener.h
+++ b/cmds/statsd/src/packages/PackageInfoListener.h
@@ -29,6 +29,9 @@ public:
// Uid map will notify this listener that the app with apk name and uid has been upgraded to
// the specified version.
virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0;
+
+ // Notify interested listeners that the given apk and uid combination no longer exits.
+ virtual void notifyAppRemoved(const std::string& apk, const int uid) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index f4621ee15dbe..d83c3a426685 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -52,25 +52,35 @@ int UidMap::getAppVersion(int uid, const string& packageName) const {
void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
const vector<String16>& packageName) {
+ updateMap(time(nullptr) * 1000000000, uid, versionCode, packageName);
+}
+
+void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
+ const vector<int32_t>& versionCode, const vector<String16>& packageName) {
lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
mMap.clear();
- for (unsigned long j = 0; j < uid.size(); j++) {
+ for (size_t j = 0; j < uid.size(); j++) {
mMap.insert(make_pair(uid[j],
AppData(string(String8(packageName[j]).string()), versionCode[j])));
}
- if (mOutput.initial_size() == 0) { // Provide the initial states in the mOutput proto
- for (unsigned long j = 0; j < uid.size(); j++) {
- auto t = mOutput.add_initial();
- t->set_app(string(String8(packageName[j]).string()));
- t->set_version(int(versionCode[j]));
- t->set_uid(uid[j]);
- }
+ auto snapshot = mOutput.add_snapshots();
+ snapshot->set_timestamp_nanos(timestamp);
+ for (size_t j = 0; j < uid.size(); j++) {
+ auto t = snapshot->add_package_info();
+ t->set_name(string(String8(packageName[j]).string()));
+ t->set_version(int(versionCode[j]));
+ t->set_uid(uid[j]);
}
}
void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
+ updateApp(time(nullptr) * 1000000000, app_16, uid, versionCode);
+}
+
+void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
+ const int32_t& versionCode) {
lock_guard<mutex> lock(mMutex);
string app = string(String8(app_16).string());
@@ -82,7 +92,7 @@ void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t
auto log = mOutput.add_changes();
log->set_deletion(false);
- // log.timestamp = TODO: choose how timestamps are computed
+ log->set_timestamp_nanos(timestamp);
log->set_app(app);
log->set_uid(uid);
log->set_version(versionCode);
@@ -102,13 +112,20 @@ void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t
}
void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
+ removeApp(time(nullptr) * 1000000000, app_16, uid);
+}
+void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
lock_guard<mutex> lock(mMutex);
string app = string(String8(app_16).string());
+ for (auto it : mSubscribers) {
+ it->notifyAppRemoved(app, uid);
+ }
+
auto log = mOutput.add_changes();
log->set_deletion(true);
- // log.timestamp = TODO: choose how timestamps are computed
+ log->set_timestamp_nanos(timestamp);
log->set_app(app);
log->set_uid(uid);
@@ -133,21 +150,67 @@ void UidMap::removeListener(sp<PackageInfoListener> producer) {
mSubscribers.erase(producer);
}
-UidMapping UidMap::getAndClearOutput() {
- lock_guard<mutex> lock(mMutex); // Lock for updates
-
- auto ret = UidMapping(mOutput); // Copy that will be returned.
+void UidMap::clearOutput() {
mOutput.Clear();
// Re-initialize the initial state for the outputs. This results in extra data being uploaded
- // but helps ensure we can't re-construct the UID->app name, versionCode mapping in server.
+ // but helps ensure we can re-construct the UID->app name, versionCode mapping in server.
+ auto snapshot = mOutput.add_snapshots();
for (auto it : mMap) {
- auto t = mOutput.add_initial();
- t->set_app(it.second.packageName);
+ auto t = snapshot->add_package_info();
+ t->set_name(it.second.packageName);
t->set_version(it.second.versionCode);
t->set_uid(it.first);
}
+}
+int64_t UidMap::getMinimumTimestampNs() {
+ int64_t m = 0;
+ for (auto it : mLastUpdatePerConfigKey) {
+ if (m == 0) {
+ m = it.second;
+ } else if (it.second < m) {
+ m = it.second;
+ }
+ }
+ return m;
+}
+
+UidMapping UidMap::getOutput(const ConfigKey& key) {
+ return getOutput(time(nullptr) * 1000000000, key);
+}
+
+UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) {
+ lock_guard<mutex> lock(mMutex); // Lock for updates
+
+ auto ret = UidMapping(mOutput); // Copy that will be returned.
+ int64_t prevMin = getMinimumTimestampNs();
+ mLastUpdatePerConfigKey[key] = timestamp;
+ int64_t newMin = getMinimumTimestampNs();
+
+ if (newMin > prevMin) {
+ int64_t cutoff_nanos = newMin;
+ auto snapshots = mOutput.mutable_snapshots();
+ auto it_snapshots = snapshots->cbegin();
+ while (it_snapshots != snapshots->cend()) {
+ if (it_snapshots->timestamp_nanos() < cutoff_nanos) {
+ // it_snapshots now points to the following element.
+ it_snapshots = snapshots->erase(it_snapshots);
+ } else {
+ ++it_snapshots;
+ }
+ }
+ auto deltas = mOutput.mutable_changes();
+ auto it_deltas = deltas->cbegin();
+ while (it_deltas != deltas->cend()) {
+ if (it_deltas->timestamp_nanos() < cutoff_nanos) {
+ // it_deltas now points to the following element.
+ it_deltas = deltas->erase(it_deltas);
+ } else {
+ ++it_deltas;
+ }
+ }
+ }
return ret;
}
@@ -160,6 +223,14 @@ void UidMap::printUidMap(FILE* out) {
}
}
+void UidMap::OnConfigUpdated(const ConfigKey& key) {
+ mLastUpdatePerConfigKey[key] = -1;
+}
+
+void UidMap::OnConfigRemoved(const ConfigKey& key) {
+ mLastUpdatePerConfigKey.erase(key);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index d550372f2e9f..bf120e04d7c8 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -17,11 +17,14 @@
#ifndef STATSD_UIDMAP_H
#define STATSD_UIDMAP_H
+#include "config/ConfigKey.h"
+#include "config/ConfigListener.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "packages/PackageInfoListener.h"
#include <binder/IResultReceiver.h>
#include <binder/IShellCallback.h>
+#include <gtest/gtest_prod.h>
#include <log/logprint.h>
#include <stdio.h>
#include <utils/RefBase.h>
@@ -51,17 +54,19 @@ public:
* All three inputs must be the same size, and the jth element in each array refers to the same
* tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
*/
+ // TODO: Add safeguards to call clearOutput if there's too much data already stored.
void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
const vector<String16>& packageName);
+ // TODO: Add safeguards to call clearOutput if there's too much data already stored.
+ void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
+ void removeApp(const String16& packageName, const int32_t& uid);
+
// Returns true if the given uid contains the specified app (eg. com.google.android.gms).
bool hasApp(int uid, const string& packageName) const;
int getAppVersion(int uid, const string& packageName) const;
- void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
- void removeApp(const String16& packageName, const int32_t& uid);
-
// Helper for debugging contents of this uid map. Can be triggered with:
// adb shell cmd stats print-uid-map
void printUidMap(FILE* out);
@@ -73,13 +78,36 @@ public:
// Remove the listener from the set of metric producers that subscribe to updates.
void removeListener(sp<PackageInfoListener> producer);
- // Grabs the current output contents and then clears it.
- UidMapping getAndClearOutput();
+ // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date.
+ void OnConfigUpdated(const ConfigKey& key);
+
+ // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date.
+ void OnConfigRemoved(const ConfigKey& key);
+
+ // Gets the output. If every config key has received the output, then the output is cleared.
+ UidMapping getOutput(const ConfigKey& key);
+
+ // Forces the output to be cleared. We still generate a snapshot based on the current state.
+ // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
+ // in case we lose a previous upload.
+ void clearOutput();
private:
+ void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
+ const vector<int32_t>& versionCode, const vector<String16>& packageName);
+
+ // TODO: Add safeguards to call clearOutput if there's too much data already stored.
+ void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
+ const int32_t& versionCode);
+ void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
+
+ UidMapping getOutput(const int64_t& timestamp, const ConfigKey& key);
+
// TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
mutable mutex mMutex;
+ // Maps uid to application data. This must be multimap since there is a feature in Android for
+ // multiple apps to share the same uid.
std::unordered_multimap<int, AppData> mMap;
// We prepare the output proto as apps are updated, so that we can grab the current output.
@@ -87,6 +115,17 @@ private:
// Metric producers that should be notified if there's an upgrade in any app.
set<sp<PackageInfoListener>> mSubscribers;
+
+ // Mapping of config keys we're aware of to the epoch time they last received an update. This
+ // lets us know it's safe to delete events older than the oldest update. The value is nanosec.
+ // Value of -1 denotes this config key has never received an upload.
+ std::unordered_map<ConfigKey, int64_t> mLastUpdatePerConfigKey;
+
+ // Returns the minimum value from mConfigKeys.
+ int64_t getMinimumTimestampNs();
+
+ // Allows unit-test to access private methods.
+ FRIEND_TEST(UidMapTest, TestClearingOutput);
};
} // namespace statsd
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
index 74ee3325a943..51244c6bdf4b 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -35,7 +35,8 @@ option java_outer_classname = "StatsEventProto";
* in the format defined here and in stats_log.proto.
*/
message StatsEvent {
- oneof event {
+ // Pushed events start at 2.
+ oneof pushed {
// 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;
@@ -69,9 +70,19 @@ message StatsEvent {
WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
WifiScanStateChanged wifi_scan_state_changed = 39;
PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
-
+ SettingChanged setting_changed = 41;
+ ActivityForegroundStateChanged activity_foreground_state_changed = 42;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
+
+ // Pulled events will start at field 1000.
+ oneof pulled {
+ WifiBytesTransferred wifi_bytes_transferred = 1000;
+ WifiBytesTransferredByFgBg wifi_bytes_transferred_by_fg_bg = 1001;
+ MobileBytesTransferred mobile_bytes_transferred = 1002;
+ MobileBytesTransferredByFgBg mobile_bytes_transferred_by_fg_bg = 1003;
+ KernelWakelocksReported kernel_wakelocks_reported = 1004;
+ }
}
/**
@@ -479,7 +490,7 @@ message ScreenBrightnessChanged {
* Logs battery level (percent full, from 0 to 100).
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message BatteryLevelChanged {
// Battery level. Should be in [0, 100].
@@ -490,7 +501,7 @@ message BatteryLevelChanged {
* Logs change in charging status of the device.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message ChargingStateChanged {
// TODO: Link directly to BatteryManager.java's constants (via a proto).
@@ -508,7 +519,7 @@ message ChargingStateChanged {
* 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
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message PluggedStateChanged {
// TODO: Link directly to BatteryManager.java's constants (via a proto).
@@ -529,7 +540,7 @@ message PluggedStateChanged {
* Logs the temperature of the device, in tenths of a degree Celsius.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message DeviceTemperatureReported {
// Temperature in tenths of a degree C.
@@ -542,7 +553,7 @@ message DeviceTemperatureReported {
* Logs when the device turns off or on.
*
* Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
*/
message DeviceOnStatusChanged {
enum State {
@@ -556,7 +567,7 @@ message DeviceOnStatusChanged {
* Logs when an app's wakeup alarm fires.
*
* Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
*/
message WakeupAlarmOccurred {
// TODO: Add attribution instead of uid?
@@ -581,7 +592,7 @@ message KernelWakeupReported {
* Logs wifi locks held by an app.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiLockStateChanged {
// TODO: Add attribution instead of uid.
@@ -598,7 +609,7 @@ message WifiLockStateChanged {
* Logs wifi signal strength changes.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiSignalStrengthChanged {
// TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
@@ -616,7 +627,7 @@ message WifiSignalStrengthChanged {
* Logs wifi scans performed by an app.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiScanStateChanged {
// TODO: Add attribution instead of uid.
@@ -633,7 +644,7 @@ message WifiScanStateChanged {
* Logs phone signal strength changes.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message PhoneSignalStrengthChanged {
// TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
@@ -645,4 +656,150 @@ message PhoneSignalStrengthChanged {
SIGNAL_STRENGTH_GREAT = 4;
}
optional SignalStrength signal_strength = 1;
-} \ No newline at end of file
+}
+
+/**
+ * Logs that a setting was updated.
+ * Logged from:
+ * frameworks/base/core/java/android/provider/Settings.java
+ * The tag and is_default allow resetting of settings to default values based on the specified
+ * tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details.
+ */
+message SettingChanged {
+ // The name of the setting.
+ optional string setting = 1;
+
+ // The change being imposed on this setting. May represent a number, eg "3".
+ optional string value = 2;
+
+ // The new value of this setting. For most settings, this is same as value. For some settings,
+ // value is +X or -X where X represents an element in a set. For example, if the previous value
+ // is A,B,C and value is -B, then new_value is A,C and prev_value is A,B,C.
+ // The +/- feature is currently only used for location_providers_allowed.
+ optional string new_value = 3;
+
+ // The previous value of this setting.
+ optional string prev_value = 4;
+
+ // The tag used with the is_default for resetting sets of settings. This is generally null.
+ optional string tag = 5;
+
+ // 1 indicates that this setting with tag should be resettable.
+ optional int32 is_default = 6;
+
+ // The user ID associated. Defined in android/os/UserHandle.java
+ optional int32 user = 7;
+}
+
+
+/*
+ * Logs activity going to foreground or background
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
+ */
+message ActivityForegroundStateChanged {
+ enum Activity {
+ MOVE_TO_BACKGROUND = 0;
+ MOVE_TO_FOREGROUND = 1;
+ }
+ optional int32 uid = 1;
+ optional string pkg_name = 2;
+ optional string class_name = 3;
+ optional Activity activity = 4;
+}
+
+/**
+ * Pulls bytes transferred via wifi (Sum of foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are wifi)
+ */
+message WifiBytesTransferred {
+ optional int32 uid = 1;
+
+ optional int64 rx_bytes = 2;
+
+ optional int64 rx_packets = 3;
+
+ optional int64 tx_bytes = 4;
+
+ optional int64 tx_packets = 5;
+}
+
+/**
+ * Pulls bytes transferred via wifi (separated by foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are wifi)
+ */
+message WifiBytesTransferredByFgBg {
+ optional int32 uid = 1;
+
+ // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+ optional int32 is_foreground = 2;
+
+ optional int64 rx_bytes = 3;
+
+ optional int64 rx_packets = 4;
+
+ optional int64 tx_bytes = 5;
+
+ optional int64 tx_packets = 6;
+}
+
+/**
+ * Pulls bytes transferred via mobile networks (Sum of foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
+ */
+message MobileBytesTransferred {
+ optional int32 uid = 1;
+
+ optional int64 rx_bytes = 2;
+
+ optional int64 rx_packets = 3;
+
+ optional int64 tx_bytes = 4;
+
+ optional int64 tx_packets = 5;
+}
+
+/**
+ * Pulls bytes transferred via mobile networks (separated by foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
+ */
+message MobileBytesTransferredByFgBg {
+ optional int32 uid = 1;
+
+ // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+ optional int32 is_foreground = 2;
+
+ optional int64 rx_bytes = 3;
+
+ optional int64 rx_packets = 4;
+
+ optional int64 tx_bytes = 5;
+
+ optional int64 tx_packets = 6;
+}
+
+/**
+ * Pulls the kernel wakelock durations. This atom is adapted from
+ * android/internal/os/KernelWakelockStats.java
+ *
+ * Pulled from:
+ * StatsCompanionService using KernelWakelockReader.
+ */
+message KernelWakelocksReported {
+ optional string name = 1;
+
+ optional int32 count = 2;
+
+ optional int32 version = 3;
+
+ optional int64 time = 4;
+}
diff --git a/cmds/statsd/src/stats_events_copy.proto b/cmds/statsd/src/stats_events_copy.proto
index 5e8ef245119a..9470372005f8 100644
--- a/cmds/statsd/src/stats_events_copy.proto
+++ b/cmds/statsd/src/stats_events_copy.proto
@@ -40,9 +40,28 @@ 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.
+ 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;
+ // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
}
@@ -76,7 +95,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.
* *****************************************************************************
*/
@@ -102,33 +121,347 @@ 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 WakelockChanged {
// 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;
+} \ No newline at end of file
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 29cd94b21b1b..1e37ff8c0aef 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -69,21 +69,53 @@ message DurationMetricData {
repeated DurationBucketInfo bucket_info = 2;
}
+message ValueBucketInfo {
+ optional int64 start_bucket_nanos = 1;
+
+ optional int64 end_bucket_nanos = 2;
+
+ optional int64 value = 3;
+}
+
+message ValueMetricData {
+ repeated KeyValuePair dimension = 1;
+
+ repeated ValueBucketInfo bucket_info = 2;
+}
+
+message GaugeBucketInfo {
+ optional int64 start_bucket_nanos = 1;
+
+ optional int64 end_bucket_nanos = 2;
+
+ optional int64 gauge = 3;
+}
+
+message GaugeMetricData {
+ repeated KeyValuePair dimension = 1;
+
+ repeated GaugeBucketInfo bucket_info = 2;
+}
+
message UidMapping {
- message AppInfo {
- optional string app = 1;
+ message PackageInfoSnapshot {
+ message PackageInfo {
+ optional string name = 1;
- optional int32 version = 2;
+ optional int32 version = 2;
- optional int32 uid = 3;
- }
+ optional int32 uid = 3;
+ }
+ optional int64 timestamp_nanos = 1;
- repeated AppInfo initial = 1;
+ repeated PackageInfo package_info = 2;
+ }
+ repeated PackageInfoSnapshot snapshots = 1;
message Change {
optional bool deletion = 1;
- optional int64 timestamp = 2;
+ optional int64 timestamp_nanos = 2;
optional string app = 3;
optional int32 uid = 4;
@@ -108,9 +140,31 @@ message StatsLogReport {
message DurationMetricDataWrapper {
repeated DurationMetricData data = 1;
}
+ message ValueMetricDataWrapper {
+ repeated ValueMetricData data = 1;
+ }
+
+ message GaugeMetricDataWrapper {
+ repeated GaugeMetricData data = 1;
+ }
+
oneof data {
EventMetricDataWrapper event_metrics = 4;
CountMetricDataWrapper count_metrics = 5;
DurationMetricDataWrapper duration_metrics = 6;
+ ValueMetricDataWrapper value_metrics = 7;
+ GaugeMetricDataWrapper gauge_metrics = 8;
}
}
+
+message ConfigMetricsReport {
+ message ConfigKey {
+ optional int32 uid = 1;
+ optional string name = 2;
+ }
+ optional ConfigKey config_key = 1;
+
+ repeated StatsLogReport metrics = 2;
+
+ optional UidMapping uid_map = 3;
+}
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 39c1d59c88b7..a428752ba634 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -40,6 +40,8 @@ const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT;
typedef std::string HashableDimensionKey;
+typedef std::map<std::string, HashableDimensionKey> ConditionKey;
+
EventMetricData parse(log_msg msg);
int getTagId(log_msg msg);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 113ac62699d2..87884b33cef0 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -82,6 +82,8 @@ message SimpleCondition {
optional bool count_nesting = 3 [default = true];
optional string stop_all = 4;
+
+ repeated KeyMatcher dimension = 5;
}
message Condition {
@@ -148,27 +150,43 @@ message CountMetric {
message DurationMetric {
optional int64 metric_id = 1;
- optional string start = 2;
-
- optional string stop = 3;
-
- optional string stop_all = 4;
+ optional string what = 2;
enum AggregationType {
DURATION_SUM = 1;
DURATION_MAX_SPARSE = 2;
- DURATION_MIN_SPARSE = 3;
}
- optional AggregationType type = 5;
+ optional AggregationType type = 3;
- optional string predicate = 6;
+ optional string predicate = 4;
- repeated KeyMatcher dimension = 7;
+ repeated KeyMatcher dimension = 5;
- optional Bucket bucket = 8;
+ optional Bucket bucket = 6;
+
+ repeated Alert alerts = 7;
+
+ repeated EventConditionLink links = 8;
- repeated EventConditionLink links = 9;
+}
+
+message GaugeMetric {
+ optional int64 metric_id = 1;
+
+ optional string what = 2;
+
+ optional int32 gauge_field = 3;
+
+ optional string condition = 4;
+
+ repeated KeyMatcher dimension = 5;
+
+ optional Bucket bucket = 6;
+
+ repeated Alert alerts = 7;
+
+ repeated EventConditionLink links = 8;
}
message ValueMetric {
@@ -184,24 +202,21 @@ message ValueMetric {
optional Bucket bucket = 6;
+ repeated Alert alerts = 7;
+
+ repeated EventConditionLink links = 8;
+
enum Operation {
- SUM_DIFF = 1;
- MIN_DIFF = 2;
- MAX_DIFF = 3;
- SUM = 4;
- MIN = 5;
- MAX = 6;
- FIRST = 7;
- LAST = 8;
+ SUM = 1;
}
- optional Operation operation = 7;
+ optional Operation operation = 9 [default = SUM];
}
message EventConditionLink {
- optional string condition = 1;
+ optional string condition = 1;
- repeated KeyMatcher key_in_main = 2;
- repeated KeyMatcher key_in_condition = 3;
+ repeated KeyMatcher key_in_main = 2;
+ repeated KeyMatcher key_in_condition = 3;
};
message StatsdConfig {
diff --git a/cmds/statsd/tests/MaxDurationTracker_test.cpp b/cmds/statsd/tests/MaxDurationTracker_test.cpp
new file mode 100644
index 000000000000..ae8bf4265235
--- /dev/null
+++ b/cmds/statsd/tests/MaxDurationTracker_test.cpp
@@ -0,0 +1,115 @@
+// 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "src/condition/ConditionWizard.h"
+#include "src/metrics/duration_helper/MaxDurationTracker.h"
+
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::os::statsd;
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+class MockConditionWizard : public ConditionWizard {
+public:
+ MOCK_METHOD2(
+ query,
+ ConditionState(const int conditionIndex,
+ const std::map<std::string, HashableDimensionKey>& conditionParameters));
+};
+
+TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucketInfo> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("", true, bucketStartTimeNs, key1);
+ tracker.noteStop("", bucketStartTimeNs + 10);
+
+ tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
+ tracker.noteStop("", bucketStartTimeNs + 40);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(20, buckets[0].duration_nanos());
+}
+
+TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucketInfo> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+ tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
+
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].duration_nanos());
+ EXPECT_EQ((long long)bucketSizeNs, buckets[1].duration_nanos());
+}
+
+TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ EXPECT_CALL(*wizard, query(_, key1)) // #4
+ .WillOnce(Return(ConditionState::kFalse));
+
+ vector<DurationBucketInfo> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int64_t durationTimeNs = 2 * 1000;
+
+ MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(5, buckets[0].duration_nanos());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index b000e1388632..e8e4d8be1cd6 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -18,6 +18,7 @@
#include "src/matchers/LogMatchingTracker.h"
#include "src/metrics/CountMetricProducer.h"
#include "src/metrics/MetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
#include "src/metrics/metrics_manager_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -124,6 +125,39 @@ StatsdConfig buildMissingMatchers() {
return config;
}
+StatsdConfig buildDimensionMetricsWithMultiTags() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("BATTERY_VERY_LOW");
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->set_tag(2);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("BATTERY_VERY_VERY_LOW");
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->set_tag(3);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("BATTERY_LOW");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("BATTERY_VERY_LOW");
+ combination->add_matcher("BATTERY_VERY_VERY_LOW");
+
+ // Count process state changes, slice by uid, while SCREEN_IS_OFF
+ CountMetric* metric = config.add_count_metric();
+ metric->set_metric_id(3);
+ metric->set_what("BATTERY_LOW");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ KeyMatcher* keyMatcher = metric->add_dimension();
+ keyMatcher->set_key(1);
+
+ return config;
+}
+
StatsdConfig buildCircleConditions() {
StatsdConfig config;
config.set_config_id(12345L);
@@ -180,6 +214,21 @@ TEST(MetricsManagerTest, TestGoodConfig) {
trackerToConditionMap));
}
+TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
+ StatsdConfig config = buildDimensionMetricsWithMultiTags();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
StatsdConfig config = buildCircleMatchers();
set<int> allTagIds;
diff --git a/cmds/statsd/tests/OringDurationTracker_test.cpp b/cmds/statsd/tests/OringDurationTracker_test.cpp
new file mode 100644
index 000000000000..0b7981935ab1
--- /dev/null
+++ b/cmds/statsd/tests/OringDurationTracker_test.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "src/condition/ConditionWizard.h"
+#include "src/metrics/duration_helper/OringDurationTracker.h"
+
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::os::statsd;
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+class MockConditionWizard : public ConditionWizard {
+public:
+ MOCK_METHOD2(
+ query,
+ ConditionState(const int conditionIndex,
+ const std::map<std::string, HashableDimensionKey>& conditionParameters));
+};
+
+TEST(OringDurationTrackerTest, TestDurationOverlap) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucketInfo> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int64_t durationTimeNs = 2 * 1000;
+
+ OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
+
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(durationTimeNs, buckets[0].duration_nanos());
+}
+
+TEST(OringDurationTrackerTest, TestDurationConditionChange) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ EXPECT_CALL(*wizard, query(_, key1)) // #4
+ .WillOnce(Return(ConditionState::kFalse));
+
+ vector<DurationBucketInfo> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int64_t durationTimeNs = 2 * 1000;
+
+ OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(5, buckets[0].duration_nanos());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index f9a90e4ee29b..671f6d4ac472 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -13,13 +13,17 @@
// limitations under the License.
#include "packages/UidMap.h"
+#include "config/ConfigKey.h"
#include <gtest/gtest.h>
#include <stdio.h>
using namespace android;
-using namespace android::os::statsd;
+
+namespace android {
+namespace os {
+namespace statsd {
#ifdef __ANDROID__
const string kApp1 = "app1.sharing.1";
@@ -64,6 +68,57 @@ TEST(UidMapTest, TestAddAndRemove) {
EXPECT_FALSE(m.hasApp(1000, kApp1));
EXPECT_TRUE(m.hasApp(1000, kApp2));
}
+
+TEST(UidMapTest, TestClearingOutput) {
+ UidMap m;
+
+ ConfigKey config1(1, "config1");
+ ConfigKey config2(1, "config2");
+
+ m.OnConfigUpdated(config1);
+
+ vector<int32_t> uids;
+ vector<int32_t> versions;
+ vector<String16> apps;
+ uids.push_back(1000);
+ uids.push_back(1000);
+ apps.push_back(String16(kApp1.c_str()));
+ apps.push_back(String16(kApp2.c_str()));
+ versions.push_back(4);
+ versions.push_back(5);
+ m.updateMap(1, uids, versions, apps);
+
+ UidMapping results = m.getOutput(2, config1);
+ EXPECT_EQ(1, results.snapshots_size());
+
+ // It should be cleared now
+ results = m.getOutput(3, config1);
+ EXPECT_EQ(0, results.snapshots_size());
+
+ // Now add another configuration.
+ m.OnConfigUpdated(config2);
+ m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
+ results = m.getOutput(6, config1);
+ EXPECT_EQ(0, results.snapshots_size());
+ EXPECT_EQ(1, results.changes_size());
+
+ // Now we still haven't been able to delete anything
+ m.updateApp(7, String16(kApp2.c_str()), 1001, 41);
+ results = m.getOutput(8, config1);
+ EXPECT_EQ(0, results.snapshots_size());
+ EXPECT_EQ(2, results.changes_size());
+
+ results = m.getOutput(9, config2);
+ EXPECT_EQ(0, results.snapshots_size());
+ EXPECT_EQ(2, results.changes_size());
+ // At this point both should be cleared.
+ EXPECT_EQ(0, m.mOutput.snapshots_size());
+ EXPECT_EQ(0, m.mOutput.changes_size());
+}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9d331a02e392..a8863bf99445 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,8 +16,6 @@
package android.app;
-import static android.os.Build.VERSION_CODES.O_MR1;
-
import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
@@ -136,6 +134,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -991,17 +990,6 @@ public class Activity extends ContextThemeWrapper
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
- if (getApplicationInfo().targetSdkVersion >= O_MR1 && mActivityInfo.isFixedOrientation()) {
- final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
- final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
- ta.recycle();
-
- if (isTranslucentOrFloating) {
- throw new IllegalStateException(
- "Only fullscreen opaque activities can request orientation");
- }
- }
-
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
@@ -7304,24 +7292,25 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Request to put this Activity in a mode where the user is locked to the
- * current task.
+ * Request to put this activity in a mode where the user is locked to a restricted set of
+ * applications.
*
- * This will prevent the user from launching other apps, going to settings, or reaching the
- * home screen. This does not include those apps whose {@link android.R.attr#lockTaskMode}
- * values permit launching while locked.
+ * <p>If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns {@code true}
+ * for this component, the current task will be launched directly into LockTask mode. Only apps
+ * whitelisted by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])} can
+ * be launched while LockTask mode is active. The user will not be able to leave this mode
+ * until this activity calls {@link #stopLockTask()}. Calling this method while the device is
+ * already in LockTask mode has no effect.
*
- * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true or
- * lockTaskMode=lockTaskModeAlways for this component then the app will go directly into
- * Lock Task mode. The user will not be able to exit this mode until
- * {@link Activity#stopLockTask()} is called.
+ * <p>Otherwise, the current task will be launched into screen pinning mode. In this case, the
+ * system will prompt the user with a dialog requesting permission to use this mode.
+ * The user can exit at any time through instructions shown on the request dialog. Calling
+ * {@link #stopLockTask()} will also terminate this mode.
*
- * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns false
- * then the system will prompt the user with a dialog requesting permission to enter
- * this mode. When entered through this method the user can exit at any time through
- * an action described by the request dialog. Calling stopLockTask will also exit the
- * mode.
+ * <p><strong>Note:</strong> this method can only be called when the activity is foreground.
+ * That is, between {@link #onResume()} and {@link #onPause()}.
*
+ * @see #stopLockTask()
* @see android.R.attr#lockTaskMode
*/
public void startLockTask() {
@@ -7332,25 +7321,24 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Allow the user to switch away from the current task.
+ * Stop the current task from being locked.
*
- * Called to end the mode started by {@link Activity#startLockTask}. This
- * can only be called by activities that have successfully called
- * startLockTask previously.
+ * <p>Called to end the LockTask or screen pinning mode started by {@link #startLockTask()}.
+ * This can only be called by activities that have called {@link #startLockTask()} previously.
*
- * This will allow the user to exit this app and move onto other activities.
- * <p>Note: This method should only be called when the activity is user-facing. That is,
- * between onResume() and onPause().
- * <p>Note: If there are other tasks below this one that are also locked then calling this
- * method will immediately finish this task and resume the previous locked one, remaining in
- * lockTask mode.
+ * <p><strong>Note:</strong> If the device is in LockTask mode that is not initially started
+ * by this activity, then calling this method will not terminate the LockTask mode, but only
+ * finish its own task. The device will remain in LockTask mode, until the activity which
+ * started the LockTask mode calls this method, or until its whitelist authorization is revoked
+ * by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])}.
*
+ * @see #startLockTask()
* @see android.R.attr#lockTaskMode
* @see ActivityManager#getLockTaskModeState()
*/
public void stopLockTask() {
try {
- ActivityManager.getService().stopLockTaskMode();
+ ActivityManager.getService().stopLockTaskModeByToken(mToken);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8d9dc1fab4ff..064e97828f06 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -682,20 +682,23 @@ public class ActivityManager {
}
/**
- * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
- * specifies the position of the created docked stack at the top half of the screen if
+ * Parameter to {@link android.app.IActivityManager#setTaskWindowingModeSplitScreenPrimary}
+ * which specifies the position of the created docked stack at the top half of the screen if
* in portrait mode or at the left half of the screen if in landscape mode.
* @hide
*/
- public static final int DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT = 0;
+ @TestApi
+ public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0;
/**
- * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
+ * Parameter to {@link android.app.IActivityManager#setTaskWindowingModeSplitScreenPrimary}
+ * which
* specifies the position of the created docked stack at the bottom half of the screen if
* in portrait mode or at the right half of the screen if in landscape mode.
* @hide
*/
- public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
+ @TestApi
+ public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
/**
* Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
@@ -1925,6 +1928,33 @@ public class ActivityManager {
}
/**
+ * Moves the input task to the primary-split-screen stack.
+ * @param taskId Id of task to move.
+ * @param createMode The mode the primary split screen stack should be created in if it doesn't
+ * exist already. See
+ * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
+ * and
+ * {@link android.app.ActivityManager
+ * #SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
+ * @param toTop If the task and stack should be moved to the top.
+ * @param animate Whether we should play an animation for the moving the task
+ * @param initialBounds If the primary stack gets created, it will use these bounds for the
+ * docked stack. Pass {@code null} to use default bounds.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public void setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+ boolean animate, Rect initialBounds) throws SecurityException {
+ try {
+ getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop, animate,
+ initialBounds);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Resizes the input stack id to the given bounds.
* @param stackId Id of the stack to resize.
* @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9d14f616a09a..a46b3c72266c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -64,6 +64,27 @@ public abstract class ActivityManagerInternal {
public static final int APP_TRANSITION_SNAPSHOT = 4;
/**
+ * The bundle key to extract the assist data.
+ */
+ public static final String ASSIST_KEY_DATA = "data";
+
+ /**
+ * The bundle key to extract the assist structure.
+ */
+ public static final String ASSIST_KEY_STRUCTURE = "structure";
+
+ /**
+ * The bundle key to extract the assist content.
+ */
+ public static final String ASSIST_KEY_CONTENT = "content";
+
+ /**
+ * The bundle key to extract the assist receiver extras.
+ */
+ public static final String ASSIST_KEY_RECEIVER_EXTRAS = "receiverExtras";
+
+
+ /**
* Grant Uri permissions from one app to another. This method only extends
* permission grants if {@code callingUid} has permission to them.
*/
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index b62e4c2d0028..4a21f5c424d5 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -16,7 +16,7 @@
package android.app;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -203,10 +203,11 @@ public class ActivityOptions {
"android.activity.taskOverlayCanResume";
/**
- * Where the docked stack should be positioned.
+ * Where the split-screen-primary stack should be positioned.
* @hide
*/
- private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
+ private static final String KEY_SPLIT_SCREEN_CREATE_MODE =
+ "android:activity.splitScreenCreateMode";
/**
* Determines whether to disallow the outgoing activity from entering picture-in-picture as the
@@ -292,7 +293,7 @@ public class ActivityOptions {
@WindowConfiguration.ActivityType
private int mLaunchActivityType = ACTIVITY_TYPE_UNDEFINED;
private int mLaunchTaskId = -1;
- private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
private boolean mDisallowEnterPictureInPictureWhileLaunching;
private boolean mTaskOverlay;
private boolean mTaskOverlayCanResume;
@@ -884,7 +885,8 @@ public class ActivityOptions {
mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
- mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
+ mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE,
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean(
KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, false);
if (opts.containsKey(KEY_ANIM_SPECS)) {
@@ -1194,13 +1196,13 @@ public class ActivityOptions {
}
/** @hide */
- public int getDockCreateMode() {
- return mDockCreateMode;
+ public int getSplitScreenCreateMode() {
+ return mSplitScreenCreateMode;
}
/** @hide */
- public void setDockCreateMode(int dockCreateMode) {
- mDockCreateMode = dockCreateMode;
+ public void setSplitScreenCreateMode(int splitScreenCreateMode) {
+ mSplitScreenCreateMode = splitScreenCreateMode;
}
/** @hide */
@@ -1369,7 +1371,7 @@ public class ActivityOptions {
b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
- b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
+ b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
mDisallowEnterPictureInPictureWhileLaunching);
if (mAnimSpecs != null) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4bd85ae9ee5c..b6fb12018201 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -254,8 +254,10 @@ public class AppOpsManager {
public static final int OP_ANSWER_PHONE_CALLS = 69;
/** @hide Run jobs when in background */
public static final int OP_RUN_ANY_IN_BACKGROUND = 70;
+ /** @hide Change Wi-Fi connectivity state */
+ public static final int OP_CHANGE_WIFI_STATE = 71;
/** @hide */
- public static final int _NUM_OP = 71;
+ public static final int _NUM_OP = 72;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -496,6 +498,7 @@ public class AppOpsManager {
OP_INSTANT_APP_START_FOREGROUND,
OP_ANSWER_PHONE_CALLS,
OP_RUN_ANY_IN_BACKGROUND,
+ OP_CHANGE_WIFI_STATE,
};
/**
@@ -574,6 +577,7 @@ public class AppOpsManager {
OPSTR_INSTANT_APP_START_FOREGROUND,
OPSTR_ANSWER_PHONE_CALLS,
null, // OP_RUN_ANY_IN_BACKGROUND
+ null, // OP_CHANGE_WIFI_STATE
};
/**
@@ -652,6 +656,7 @@ public class AppOpsManager {
"INSTANT_APP_START_FOREGROUND",
"ANSWER_PHONE_CALLS",
"RUN_ANY_IN_BACKGROUND",
+ "CHANGE_WIFI_STATE",
};
/**
@@ -730,6 +735,7 @@ public class AppOpsManager {
Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
Manifest.permission.ANSWER_PHONE_CALLS,
null, // no permission for OP_RUN_ANY_IN_BACKGROUND
+ Manifest.permission.CHANGE_WIFI_STATE,
};
/**
@@ -809,6 +815,7 @@ public class AppOpsManager {
null, // INSTANT_APP_START_FOREGROUND
null, // ANSWER_PHONE_CALLS
null, // OP_RUN_ANY_IN_BACKGROUND
+ null, // OP_CHANGE_WIFI_STATE
};
/**
@@ -887,6 +894,7 @@ public class AppOpsManager {
false, // INSTANT_APP_START_FOREGROUND
false, // ANSWER_PHONE_CALLS
false, // OP_RUN_ANY_IN_BACKGROUND
+ false, // OP_CHANGE_WIFI_STATE
};
/**
@@ -964,6 +972,7 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // OP_INSTANT_APP_START_FOREGROUND
AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
AppOpsManager.MODE_ALLOWED, // OP_RUN_ANY_IN_BACKGROUND
+ AppOpsManager.MODE_ALLOWED, // OP_CHANGE_WIFI_STATE
};
/**
@@ -1045,6 +1054,7 @@ public class AppOpsManager {
false,
false, // ANSWER_PHONE_CALLS
false, // OP_RUN_ANY_IN_BACKGROUND
+ false, // OP_CHANGE_WIFI_STATE
};
/**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 86b8402338e9..388459fbbc6e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -63,6 +63,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.StrictMode;
import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -82,7 +83,7 @@ interface IActivityManager {
// below block of transactions.
// Since these transactions are also called from native code, these must be kept in sync with
- // the ones in frameworks/native/include/binder/IActivityManager.h
+ // the ones in frameworks/native/libs/binder/include/binder/IActivityManager.h
// =============== Beginning of transactions used on native side as well ======================
ParcelFileDescriptor openContentUri(in String uriString);
// =============== End of transactions used on native side as well ============================
@@ -231,7 +232,7 @@ interface IActivityManager {
boolean requireFull, in String name, in String callerPackage);
void addPackageDependency(in String packageName);
void killApplication(in String pkg, int appId, int userId, in String reason);
- void closeSystemDialogs(in String reason);
+ oneway void closeSystemDialogs(in String reason);
Debug.MemoryInfo[] getProcessMemoryInfo(in int[] pids);
void killApplicationProcess(in String processName, int uid);
int startActivityIntentSender(in IApplicationThread caller,
@@ -412,7 +413,7 @@ interface IActivityManager {
String getTagForIntentSender(in IIntentSender sender, in String prefix);
boolean startUserInBackground(int userid);
void startLockTaskModeByToken(in IBinder token);
- void stopLockTaskMode();
+ void stopLockTaskModeByToken(in IBinder token);
boolean isInLockTaskMode();
void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
int startVoiceActivity(in String callingPackage, int callingPid, int callingUid,
@@ -421,6 +422,9 @@ interface IActivityManager {
in Bundle options, int userId);
int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
in Intent intent, in String resolvedType, in Bundle options, int userId);
+ int startRecentsActivity(in IAssistDataReceiver assistDataReceiver, in Bundle options,
+ int userId);
+ int startActivityFromRecents(int taskId, in Bundle options);
Bundle getActivityOptions(in IBinder token);
List<IBinder> getAppTasks(in String callingPackage);
void startSystemLockTaskMode(int taskId);
@@ -428,7 +432,6 @@ interface IActivityManager {
void finishVoiceTask(in IVoiceInteractionSession session);
boolean isTopOfTask(in IBinder token);
void notifyLaunchTaskBehindComplete(in IBinder token);
- int startActivityFromRecents(int taskId, in Bundle options);
void notifyEnterAnimationComplete(in IBinder token);
int startActivityAsCaller(in IApplicationThread caller, in String callingPackage,
in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
@@ -447,13 +450,12 @@ interface IActivityManager {
int checkPermissionWithToken(in String permission, int pid, int uid,
in IBinder callerToken);
void registerTaskStackListener(in ITaskStackListener listener);
+ void unregisterTaskStackListener(in ITaskStackListener listener);
-
- // Start of M transactions
void notifyCleartextNetwork(int uid, in byte[] firstPacket);
int createStackOnDisplay(int displayId);
void setTaskResizeable(int taskId, int resizeableMode);
- boolean requestAssistContextExtras(int requestType, in IResultReceiver receiver,
+ boolean requestAssistContextExtras(int requestType, in IAssistDataReceiver receiver,
in Bundle receiverExtras, in IBinder activityToken,
boolean focused, boolean newSessionId);
void resizeTask(int taskId, in Rect bounds, int resizeMode);
@@ -499,8 +501,8 @@ interface IActivityManager {
void exitFreeformMode(in IBinder token);
void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
- boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
- in Rect initialBounds);
+ boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+ boolean animate, in Rect initialBounds);
/**
* Dismisses split-screen multi-window mode.
* {@param toTop} If true the current primary split-screen stack will be placed or left on top.
@@ -615,9 +617,8 @@ interface IActivityManager {
* @return Returns true if the configuration was updated.
*/
boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId);
- void unregisterTaskStackListener(ITaskStackListener listener);
void moveStackToDisplay(int stackId, int displayId);
- boolean requestAutofillData(in IResultReceiver receiver, in Bundle receiverExtras,
+ boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras,
in IBinder activityToken, int flags);
void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback);
int restartUserInBackground(int userId);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index d4752a771492..9e926bd63a6a 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -138,6 +138,7 @@ interface INotificationManager
void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
void setNotificationPolicyAccessGranted(String pkg, boolean granted);
+ void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
AutomaticZenRule getAutomaticZenRule(String id);
List<ZenModeConfig.ZenRule> getZenRules();
String addAutomaticZenRule(in AutomaticZenRule automaticZenRule);
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 5c6ffa39e6f7..392387a99897 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -198,4 +198,20 @@ public class VrManager {
e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Sets the current standby status of the VR device. Standby mode is only used on standalone vr
+ * devices. Standby mode is a deep sleep state where it's appropriate to turn off vr mode.
+ *
+ * @param standby True if the device is entering standby, false if it's exiting standby.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_VR_MANAGER)
+ public void setStandbyEnabled(boolean standby) {
+ try {
+ mService.setStandbyEnabled(standby);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 1cde73a0af61..b640bd5bee2a 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -18,6 +18,7 @@ package android.app.job;
import static android.util.TimeUtils.formatDuration;
+import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -71,6 +72,9 @@ public class JobInfo implements Parcelable {
/** This job requires metered connectivity such as most cellular data networks. */
public static final int NETWORK_TYPE_METERED = 4;
+ /** Sentinel value indicating that bytes are unknown. */
+ public static final int NETWORK_BYTES_UNKNOWN = -1;
+
/**
* Amount of backoff a job has initially by default, in milliseconds.
*/
@@ -250,6 +254,7 @@ public class JobInfo implements Parcelable {
private final boolean hasEarlyConstraint;
private final boolean hasLateConstraint;
private final int networkType;
+ private final long networkBytes;
private final long minLatencyMillis;
private final long maxExecutionDelayMillis;
private final boolean isPeriodic;
@@ -387,6 +392,18 @@ public class JobInfo implements Parcelable {
}
/**
+ * Return the estimated size of network traffic that will be performed by
+ * this job, in bytes.
+ *
+ * @return Estimated size of network traffic, or
+ * {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+ * @see Builder#setEstimatedNetworkBytes(long)
+ */
+ public @BytesLong long getEstimatedNetworkBytes() {
+ return networkBytes;
+ }
+
+ /**
* Set for a job that does not recur periodically, to specify a delay after which the job
* will be eligible for execution. This value is not set if the job recurs periodically.
*/
@@ -524,6 +541,9 @@ public class JobInfo implements Parcelable {
if (networkType != j.networkType) {
return false;
}
+ if (networkBytes != j.networkBytes) {
+ return false;
+ }
if (minLatencyMillis != j.minLatencyMillis) {
return false;
}
@@ -582,6 +602,7 @@ public class JobInfo implements Parcelable {
hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint);
hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint);
hashCode = 31 * hashCode + networkType;
+ hashCode = 31 * hashCode + Long.hashCode(networkBytes);
hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -612,6 +633,7 @@ public class JobInfo implements Parcelable {
triggerContentUpdateDelay = in.readLong();
triggerContentMaxDelay = in.readLong();
networkType = in.readInt();
+ networkBytes = in.readLong();
minLatencyMillis = in.readLong();
maxExecutionDelayMillis = in.readLong();
isPeriodic = in.readInt() == 1;
@@ -640,6 +662,7 @@ public class JobInfo implements Parcelable {
triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
triggerContentMaxDelay = b.mTriggerContentMaxDelay;
networkType = b.mNetworkType;
+ networkBytes = b.mNetworkBytes;
minLatencyMillis = b.mMinLatencyMillis;
maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
isPeriodic = b.mIsPeriodic;
@@ -677,6 +700,7 @@ public class JobInfo implements Parcelable {
out.writeLong(triggerContentUpdateDelay);
out.writeLong(triggerContentMaxDelay);
out.writeInt(networkType);
+ out.writeLong(networkBytes);
out.writeLong(minLatencyMillis);
out.writeLong(maxExecutionDelayMillis);
out.writeInt(isPeriodic ? 1 : 0);
@@ -810,6 +834,7 @@ public class JobInfo implements Parcelable {
// Requirements.
private int mConstraintFlags;
private int mNetworkType;
+ private long mNetworkBytes = NETWORK_BYTES_UNKNOWN;
private ArrayList<TriggerContentUri> mTriggerContentUris;
private long mTriggerContentUpdateDelay = -1;
private long mTriggerContentMaxDelay = -1;
@@ -931,6 +956,43 @@ public class JobInfo implements Parcelable {
}
/**
+ * Set the estimated size of network traffic that will be performed by
+ * this job, in bytes.
+ * <p>
+ * Apps are encouraged to provide values that are as accurate as
+ * possible, but when the exact size isn't available, an
+ * order-of-magnitude estimate can be provided instead. Here are some
+ * specific examples:
+ * <ul>
+ * <li>A job that is backing up a photo knows the exact size of that
+ * photo, so it should provide that size as the estimate.
+ * <li>A job that refreshes top news stories wouldn't know an exact
+ * size, but if the size is expected to be consistently around 100KB, it
+ * can provide that order-of-magnitude value as the estimate.
+ * <li>A job that synchronizes email could end up using an extreme range
+ * of data, from under 1KB when nothing has changed, to dozens of MB
+ * when there are new emails with attachments. Jobs that cannot provide
+ * reasonable estimates should leave this estimated value undefined.
+ * </ul>
+ * Note that the system may choose to delay jobs with large network
+ * usage estimates when the device has a poor network connection, in
+ * order to save battery.
+ *
+ * @param networkBytes The estimated size of network traffic that will
+ * be performed by this job, in bytes. This value only
+ * reflects the traffic that will be performed by the base
+ * job; if you're using {@link JobWorkItem} then you also
+ * need to define the network traffic used by each work item
+ * when constructing them.
+ * @see JobInfo#getEstimatedNetworkBytes()
+ * @see JobWorkItem#JobWorkItem(android.content.Intent, long)
+ */
+ public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
+ mNetworkBytes = networkBytes;
+ return this;
+ }
+
+ /**
* Specify that to run this job, the device must be charging (or be a
* non-battery-powered device connected to permanent power, such as Android TV
* devices). This defaults to {@code false}.
@@ -1156,6 +1218,11 @@ public class JobInfo implements Parcelable {
throw new IllegalArgumentException("You're trying to build a job with no " +
"constraints, this is not allowed.");
}
+ // Check that network estimates require network type
+ if (mNetworkBytes > 0 && mNetworkType == NETWORK_TYPE_NONE) {
+ throw new IllegalArgumentException(
+ "Can't provide estimated network usage without requiring a network");
+ }
// Check that a deadline was not set on a periodic job.
if (mIsPeriodic) {
if (mMaxExecutionDelayMillis != 0L) {
diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java
index 0eb0450e8f2a..1c46e8ecbe52 100644
--- a/core/java/android/app/job/JobWorkItem.java
+++ b/core/java/android/app/job/JobWorkItem.java
@@ -16,6 +16,7 @@
package android.app.job;
+import android.annotation.BytesLong;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,6 +28,7 @@ import android.os.Parcelable;
*/
final public class JobWorkItem implements Parcelable {
final Intent mIntent;
+ final long mNetworkBytes;
int mDeliveryCount;
int mWorkId;
Object mGrants;
@@ -39,6 +41,22 @@ final public class JobWorkItem implements Parcelable {
*/
public JobWorkItem(Intent intent) {
mIntent = intent;
+ mNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ }
+
+ /**
+ * Create a new piece of work, which can be submitted to
+ * {@link JobScheduler#enqueue JobScheduler.enqueue}.
+ *
+ * @param intent The general Intent describing this work.
+ * @param networkBytes The estimated size of network traffic that will be
+ * performed by this job work item, in bytes. See
+ * {@link JobInfo.Builder#setEstimatedNetworkBytes(long)} for
+ * details about how to estimate.
+ */
+ public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+ mIntent = intent;
+ mNetworkBytes = networkBytes;
}
/**
@@ -49,6 +67,17 @@ final public class JobWorkItem implements Parcelable {
}
/**
+ * Return the estimated size of network traffic that will be performed by
+ * this job work item, in bytes.
+ *
+ * @return estimated size, or {@link JobInfo#NETWORK_BYTES_UNKNOWN} when
+ * unknown.
+ */
+ public @BytesLong long getEstimatedNetworkBytes() {
+ return mNetworkBytes;
+ }
+
+ /**
* Return the count of the number of times this work item has been delivered
* to the job. The value will be > 1 if it has been redelivered because the job
* was stopped or crashed while it had previously been delivered but before the
@@ -99,6 +128,10 @@ final public class JobWorkItem implements Parcelable {
sb.append(mWorkId);
sb.append(" intent=");
sb.append(mIntent);
+ if (mNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ sb.append(" networkBytes=");
+ sb.append(mNetworkBytes);
+ }
if (mDeliveryCount != 0) {
sb.append(" dcount=");
sb.append(mDeliveryCount);
@@ -118,6 +151,7 @@ final public class JobWorkItem implements Parcelable {
} else {
out.writeInt(0);
}
+ out.writeLong(mNetworkBytes);
out.writeInt(mDeliveryCount);
out.writeInt(mWorkId);
}
@@ -139,6 +173,7 @@ final public class JobWorkItem implements Parcelable {
} else {
mIntent = null;
}
+ mNetworkBytes = in.readLong();
mDeliveryCount = in.readInt();
mWorkId = in.readInt();
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 33825b4b5f41..da718dc9a2a2 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -19,15 +19,19 @@ import android.Manifest.permission;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
+import android.os.UserHandle;
import android.util.Log;
import java.util.concurrent.CountDownLatch;
@@ -143,9 +147,13 @@ public abstract class SliceProvider extends ContentProvider {
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (method.equals(METHOD_SLICE)) {
- getContext().enforceCallingPermission(permission.BIND_SLICE,
- "Slice binding requires the permission BIND_SLICE");
Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+ getContext().enforceUriPermission(uri, permission.BIND_SLICE,
+ permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires the permission BIND_SLICE");
+ }
Slice s = handleBindSlice(uri);
Bundle b = new Bundle();
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index c827432a8b0b..3a3e16e0956c 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -261,7 +261,10 @@ public final class UsageStatsManager {
/**
* @hide
+ * Changes the app standby state to the provided bucket.
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE)
public void setAppStandbyBucket(String packageName, @StandbyBuckets int bucket) {
try {
mService.setAppStandbyBucket(packageName, bucket, mContext.getUserId());
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 2c124034b786..243ad359a48f 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -120,7 +120,7 @@ public class BluetoothGattCharacteristic implements Parcelable {
public static final int WRITE_TYPE_DEFAULT = 0x02;
/**
- * Wrtite characteristic without requiring a response by the remote device
+ * Write characteristic without requiring a response by the remote device
*/
public static final int WRITE_TYPE_NO_RESPONSE = 0x01;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e47de752ec70..dd729a36875d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3444,11 +3444,12 @@ public class Intent implements Parcelable, Cloneable {
/**
* A broadcast action to trigger a factory reset.
*
- * <p> The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission.
+ * <p>The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. The
+ * reason for the factory reset should be specified as {@link #EXTRA_REASON}.
*
* <p>Not for use by third-party applications.
*
- * @see #EXTRA_FORCE_MASTER_CLEAR
+ * @see #EXTRA_FORCE_FACTORY_RESET
*
* {@hide}
*/
@@ -4827,7 +4828,13 @@ public class Intent implements Parcelable, Cloneable {
/** @hide */
public static final int EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT = 2;
- /** {@hide} */
+ /**
+ * Intent extra: the reason that the operation associated with this intent is being performed.
+ *
+ * <p>Type: String
+ * @hide
+ */
+ @SystemApi
public static final String EXTRA_REASON = "android.intent.extra.REASON";
/**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 41667c4c3ce3..837c00a72784 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -455,7 +455,6 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
public static final int FLAG_TURN_SCREEN_ON = 0x1000000;
-
/**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
@@ -1001,20 +1000,12 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* Returns true if the activity's orientation is fixed.
* @hide
*/
- public boolean isFixedOrientation() {
+ boolean isFixedOrientation() {
return isFixedOrientationLandscape() || isFixedOrientationPortrait()
|| screenOrientation == SCREEN_ORIENTATION_LOCKED;
}
/**
- * Returns true if the specified orientation is considered fixed.
- * @hide
- */
- static public boolean isFixedOrientation(int orientation) {
- return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation);
- }
-
- /**
* Returns true if the activity's orientation is fixed to landscape.
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b48829cfc87d..1c5cf15da062 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -102,6 +102,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -1708,13 +1709,33 @@ public class PackageParser {
*/
public static ApkLite parseApkLite(File apkFile, int flags)
throws PackageParserException {
- final String apkPath = apkFile.getAbsolutePath();
+ return parseApkLiteInner(apkFile, null, null, flags);
+ }
+
+ /**
+ * Utility method that retrieves lightweight details about a single APK
+ * file, including package name, split name, and install location.
+ *
+ * @param fd already open file descriptor of an apk file
+ * @param debugPathName arbitrary text name for this file, for debug output
+ * @param flags optional parse flags, such as
+ * {@link #PARSE_COLLECT_CERTIFICATES}
+ */
+ public static ApkLite parseApkLite(FileDescriptor fd, String debugPathName, int flags)
+ throws PackageParserException {
+ return parseApkLiteInner(null, fd, debugPathName, flags);
+ }
+
+ private static ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, String debugPathName,
+ int flags) throws PackageParserException {
+ final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
AssetManager assets = null;
XmlResourceParser parser = null;
try {
assets = newConfiguredAssetManager();
- int cookie = assets.addAssetPath(apkPath);
+ int cookie = fd != null
+ ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath);
if (cookie == 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index f0adcd6cfb3e..78665609bdd4 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -28,8 +28,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
-import dalvik.annotation.optimization.FastNative;
-
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -694,7 +693,35 @@ public final class AssetManager implements AutoCloseable {
private native final int addAssetPathNative(String path, boolean appAsLib);
- /**
+ /**
+ * Add an additional set of assets to the asset manager from an already open
+ * FileDescriptor. Not for use by applications.
+ * This does not give full AssetManager functionality for these assets,
+ * since the origin of the file is not known for purposes of sharing,
+ * overlay resolution, and other features. However it does allow you
+ * to do simple access to the contents of the given fd as an apk file.
+ * Performs a dup of the underlying fd, so you must take care of still closing
+ * the FileDescriptor yourself (and can do that whenever you want).
+ * Returns the cookie of the added asset, or 0 on failure.
+ * {@hide}
+ */
+ public int addAssetFd(FileDescriptor fd, String debugPathName) {
+ return addAssetFdInternal(fd, debugPathName, false);
+ }
+
+ private int addAssetFdInternal(FileDescriptor fd, String debugPathName,
+ boolean appAsLib) {
+ synchronized (this) {
+ int res = addAssetFdNative(fd, debugPathName, appAsLib);
+ makeStringBlocks(mStringBlocks);
+ return res;
+ }
+ }
+
+ private native int addAssetFdNative(FileDescriptor fd, String debugPathName,
+ boolean appAsLib);
+
+ /**
* Add a set of assets to overlay an already added set of assets.
*
* This is only intended for application resources. System wide resources
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 28e9fce3048d..6a4aae66c848 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -80,13 +80,15 @@ public class FontResourcesParser {
private int mWeight;
private int mItalic;
private int mTtcIndex;
+ private String mVariationSettings;
private int mResourceId;
public FontFileResourceEntry(@NonNull String fileName, int weight, int italic,
- int ttcIndex) {
+ @Nullable String variationSettings, int ttcIndex) {
mFileName = fileName;
mWeight = weight;
mItalic = italic;
+ mVariationSettings = variationSettings;
mTtcIndex = ttcIndex;
}
@@ -102,6 +104,10 @@ public class FontResourcesParser {
return mItalic;
}
+ public @Nullable String getVariationSettings() {
+ return mVariationSettings;
+ }
+
public int getTtcIndex() {
return mTtcIndex;
}
@@ -211,6 +217,8 @@ public class FontResourcesParser {
Typeface.RESOLVE_BY_FONT_TABLE);
int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
Typeface.RESOLVE_BY_FONT_TABLE);
+ String variationSettings = array.getString(
+ R.styleable.FontFamilyFont_fontVariationSettings);
int ttcIndex = array.getInt(R.styleable.FontFamilyFont_ttcIndex, 0);
String filename = array.getString(R.styleable.FontFamilyFont_font);
array.recycle();
@@ -220,7 +228,7 @@ public class FontResourcesParser {
if (filename == null) {
return null;
}
- return new FontFileResourceEntry(filename, weight, italic, ttcIndex);
+ return new FontFileResourceEntry(filename, weight, italic, variationSettings, ttcIndex);
}
private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 386239cf4f93..3239212adf66 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -49,6 +49,8 @@ import android.util.TypedValue;
import android.util.Xml;
import android.view.DisplayAdjustments;
+import com.android.internal.util.GrowingArrayUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -117,6 +119,13 @@ public class ResourcesImpl {
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
new ConfigurationBoundResourceCache<>();
+ // A stack of all the resourceIds already referenced when parsing a resource. This is used to
+ // detect circular references in the xml.
+ // Using a ThreadLocal variable ensures that we have different stacks for multiple parallel
+ // calls to ResourcesImpl
+ private final ThreadLocal<LookupStack> mLookupStack =
+ ThreadLocal.withInitial(() -> new LookupStack());
+
/** Size of the cyclical cache used to map XML files to blocks. */
private static final int XML_BLOCK_CACHE_SIZE = 4;
@@ -784,19 +793,29 @@ public class ResourcesImpl {
final Drawable dr;
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ LookupStack stack = mLookupStack.get();
try {
- if (file.endsWith(".xml")) {
- final XmlResourceParser rp = loadXmlResourceParser(
- file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
- rp.close();
- } else {
- final InputStream is = mAssets.openNonAsset(
- value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
- is.close();
+ // Perform a linear search to check if we have already referenced this resource before.
+ if (stack.contains(id)) {
+ throw new Exception("Recursive reference in drawable");
}
- } catch (Exception | StackOverflowError e) {
+ stack.push(id);
+ try {
+ if (file.endsWith(".xml")) {
+ final XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "drawable");
+ dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
+ rp.close();
+ } else {
+ final InputStream is = mAssets.openNonAsset(
+ value.assetCookie, file, AssetManager.ACCESS_STREAMING);
+ dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
+ is.close();
+ }
+ } finally {
+ stack.pop();
+ }
+ } catch (Exception e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
final NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
@@ -1377,4 +1396,29 @@ public class ResourcesImpl {
}
}
}
+
+ private static class LookupStack {
+
+ // Pick a reasonable default size for the array, it is grown as needed.
+ private int[] mIds = new int[4];
+ private int mSize = 0;
+
+ public void push(int id) {
+ mIds = GrowingArrayUtils.append(mIds, mSize, id);
+ mSize++;
+ }
+
+ public boolean contains(int id) {
+ for (int i = 0; i < mSize; i++) {
+ if (mIds[i] == id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void pop() {
+ mSize--;
+ }
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index c28583ea867a..361b81b77f1d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -289,7 +289,9 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
+ boolean walEnabled =
+ (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
+ if (walEnabled || mConfiguration.useCompatibilityWal) {
setJournalMode("WAL");
setSyncMode(SQLiteGlobal.getWALSyncMode());
} else {
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 2dc5ca43e7a1..13e6f7182e8a 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -22,6 +22,8 @@ import android.database.DatabaseUtils;
import android.os.StrictMode;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.util.HashMap;
import java.util.Map;
@@ -60,6 +62,9 @@ public class SQLiteCursor extends AbstractWindowedCursor {
/** Used to find out where a cursor was allocated in case it never got released. */
private final Throwable mStackTrace;
+ /** Controls fetching of rows relative to requested position **/
+ private boolean mFillWindowForwardOnly;
+
/**
* Execute a query and provide access to its result set through a Cursor
* interface. For a query such as: {@code SELECT name, birth, phone FROM
@@ -136,18 +141,19 @@ public class SQLiteCursor extends AbstractWindowedCursor {
private void fillWindow(int requiredPos) {
clearOrCreateWindow(getDatabase().getPath());
-
try {
+ Preconditions.checkArgumentNonnegative(requiredPos,
+ "requiredPos cannot be negative, but was " + requiredPos);
+
if (mCount == NO_COUNT) {
- int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
- mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
+ mCount = mQuery.fillWindow(mWindow, requiredPos, requiredPos, true);
mCursorWindowCapacity = mWindow.getNumRows();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
}
} else {
- int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
- mCursorWindowCapacity);
+ int startPos = mFillWindowForwardOnly ? requiredPos : DatabaseUtils
+ .cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity);
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
}
} catch (RuntimeException ex) {
@@ -252,6 +258,20 @@ public class SQLiteCursor extends AbstractWindowedCursor {
}
/**
+ * Controls fetching of rows relative to requested position.
+ *
+ * <p>Calling this method defines how rows will be loaded, but it doesn't affect rows that
+ * are already in the window. This setting is preserved if a new window is
+ * {@link #setWindow(CursorWindow) set}
+ *
+ * @param fillWindowForwardOnly if true, rows will be fetched starting from requested position
+ * up to the window's capacity. Default value is false.
+ */
+ public void setFillWindowForwardOnly(boolean fillWindowForwardOnly) {
+ mFillWindowForwardOnly = fillWindowForwardOnly;
+ }
+
+ /**
* Release the native resources, if they haven't been released yet.
*/
@Override
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index df0e262b712f..83b8dc76bfc3 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -285,6 +285,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
}
}
mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs;
+ mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported();
}
@Override
@@ -2070,15 +2071,21 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
- if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
+ final boolean oldUseCompatibilityWal = mConfigurationLocked.useCompatibilityWal;
+ final int oldFlags = mConfigurationLocked.openFlags;
+ if (!oldUseCompatibilityWal && (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
return;
}
mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
+ // If an app explicitly disables WAL, do not even use compatibility mode
+ mConfigurationLocked.useCompatibilityWal = false;
+
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
- mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
+ mConfigurationLocked.openFlags = oldFlags;
+ mConfigurationLocked.useCompatibilityWal = oldUseCompatibilityWal;
throw ex;
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 34c9b3395d1a..905da7247308 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -111,6 +111,15 @@ public final class SQLiteDatabaseConfiguration {
public long idleConnectionTimeoutMs = Long.MAX_VALUE;
/**
+ * Enables compatibility WAL mode. Applications cannot explicitly choose compatibility WAL mode,
+ * therefore it is not exposed as a flag.
+ *
+ * <p>In this mode, only database journal mode will be changed, connection pool
+ * size will still be limited to a single connection.
+ */
+ public boolean useCompatibilityWal;
+
+ /**
* Creates a database configuration with the required parameters for opening a
* database and default values for all other parameters.
*
@@ -170,6 +179,7 @@ public final class SQLiteDatabaseConfiguration {
lookasideSlotSize = other.lookasideSlotSize;
lookasideSlotCount = other.lookasideSlotCount;
idleConnectionTimeoutMs = other.idleConnectionTimeoutMs;
+ useCompatibilityWal = other.useCompatibilityWal;
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index 94d5555c4c24..bb2a51706767 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -81,6 +81,17 @@ public final class SQLiteGlobal {
}
/**
+ * Returns true if compatibility WAL mode is supported. In this mode, only
+ * database journal mode is changed. Connection pool will use at most one connection.
+ * @hide
+ */
+ public static boolean isCompatibilityWalSupported() {
+ return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported",
+ Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.db_compatibility_wal_supported));
+ }
+
+ /**
* Gets the journal size limit in bytes.
*/
public static int getJournalSizeLimit() {
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 996824d4393d..6ce96698e444 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";
/**
- * Activity intent sent when a USB device is attached.
+ * Activity intent sent when user attaches a USB device.
*
* This intent is sent when a USB device is attached to the USB bus when in host mode.
* <ul>
@@ -128,7 +128,7 @@ public class UsbManager {
"android.hardware.usb.action.USB_DEVICE_DETACHED";
/**
- * Activity intent sent when a USB accessory is attached.
+ * Activity intent sent when user attaches a USB accessory.
*
* <ul>
* <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 3c868c399b1f..903b602b42f8 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,14 +16,14 @@
package android.net;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import libcore.net.http.Dns;
-import libcore.net.http.HttpURLConnectionFactory;
+import com.android.okhttp.internalandroidapi.Dns;
+import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -34,11 +34,12 @@ import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
-import java.net.UnknownHostException;
import java.net.URL;
import java.net.URLConnection;
+import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
+
import javax.net.SocketFactory;
/**
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 0f1e2597f39e..d1ce60e9ff08 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,6 +1,6 @@
ek@google.com
hugobenichi@google.com
-jsharkey@google.com
+jsharkey@android.com
lorenzo@google.com
satk@google.com
silberst@google.com
diff --git a/core/java/android/os/BatteryStatsInternal.java b/core/java/android/os/BatteryStatsInternal.java
new file mode 100644
index 000000000000..b0436eb5f8af
--- /dev/null
+++ b/core/java/android/os/BatteryStatsInternal.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Battery stats local system service interface. This is used to pass internal data out of
+ * BatteryStatsImpl.
+ *
+ * @hide Only for use within Android OS.
+ */
+public abstract class BatteryStatsInternal {
+ /**
+ * Returns the wifi interfaces.
+ */
+ public abstract String[] getWifiIfaces();
+
+ /**
+ * Returns the mobile data interfaces.
+ */
+ public abstract String[] getMobileIfaces();
+}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 017c2134f288..2e62eb6a5f97 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -23,7 +23,6 @@ import android.util.Log;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.TypedProperties;
-import dalvik.bytecode.OpcodeInfo;
import dalvik.system.VMDebug;
import org.apache.harmony.dalvik.ddmc.Chunk;
@@ -48,8 +47,6 @@ import java.util.HashMap;
import java.util.Map;
-
-
/**
* Provides various debugging methods for Android applications, including
* tracing and allocation counts.
@@ -1959,13 +1956,7 @@ public final class Debug
*/
@Deprecated
public static class InstructionCount {
- private static final int NUM_INSTR =
- OpcodeInfo.MAXIMUM_PACKED_VALUE + 1;
-
- private int[] mCounts;
-
public InstructionCount() {
- mCounts = new int[NUM_INSTR];
}
/**
@@ -1975,13 +1966,7 @@ public final class Debug
* @return true if counting was started
*/
public boolean resetAndStart() {
- try {
- VMDebug.startInstructionCounting();
- VMDebug.resetInstructionCount();
- } catch (UnsupportedOperationException uoe) {
- return false;
- }
- return true;
+ return false;
}
/**
@@ -1989,13 +1974,7 @@ public final class Debug
* counting process.
*/
public boolean collect() {
- try {
- VMDebug.stopInstructionCounting();
- VMDebug.getInstructionCount(mCounts);
- } catch (UnsupportedOperationException uoe) {
- return false;
- }
- return true;
+ return false;
}
/**
@@ -2003,13 +1982,7 @@ public final class Debug
* all threads).
*/
public int globalTotal() {
- int count = 0;
-
- for (int i = 0; i < NUM_INSTR; i++) {
- count += mCounts[i];
- }
-
- return count;
+ return 0;
}
/**
@@ -2017,15 +1990,7 @@ public final class Debug
* executed globally.
*/
public int globalMethodInvocations() {
- int count = 0;
-
- for (int i = 0; i < NUM_INSTR; i++) {
- if (OpcodeInfo.isInvoke(i)) {
- count += mCounts[i];
- }
- }
-
- return count;
+ return 0;
}
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 5b0e5bbce2f7..f977c1dea438 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -836,7 +836,6 @@ public class Environment {
* physically removed.
*/
public static boolean isExternalStorageRemovable() {
- if (isStorageDisabled()) return false;
final File externalDir = sCurrentUser.getExternalDirs()[0];
return isExternalStorageRemovable(externalDir);
}
@@ -875,7 +874,6 @@ public class Environment {
* boolean)
*/
public static boolean isExternalStorageEmulated() {
- if (isStorageDisabled()) return false;
final File externalDir = sCurrentUser.getExternalDirs()[0];
return isExternalStorageEmulated(externalDir);
}
@@ -951,9 +949,6 @@ public class Environment {
return cur;
}
- private static boolean isStorageDisabled() {
- return SystemProperties.getBoolean("config.disable_storage", false);
- }
/**
* If the given path exists on emulated external storage, return the
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 7dec4d724f15..3544ea1e03e3 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -156,4 +156,27 @@ public class HidlSupport {
// Should not reach here.
throw new UnsupportedOperationException();
}
+
+ /**
+ * Test that two interfaces are equal. This is the Java equivalent to C++
+ * interfacesEqual function.
+ * This essentially calls .equals on the internal binder objects (via Binder()).
+ * - If both interfaces are proxies, asBinder() returns a {@link HwRemoteBinder}
+ * object, and they are compared in {@link HwRemoteBinder#equals}.
+ * - If both interfaces are stubs, asBinder() returns the object itself. By default,
+ * auto-generated IFoo.Stub does not override equals(), but an implementation can
+ * optionally override it, and {@code interfacesEqual} will use it here.
+ */
+ public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
+ if (lft == rgt) {
+ return true;
+ }
+ if (lft == null || rgt == null) {
+ return false;
+ }
+ if (!(rgt instanceof IHwInterface)) {
+ return false;
+ }
+ return Objects.equals(lft.asBinder(), ((IHwInterface) rgt).asBinder());
+ }
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 270e63f408a7..dd9e774141e1 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -16,10 +16,10 @@
package android.os;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
import libcore.util.NativeAllocationRegistry;
+import java.util.NoSuchElementException;
+
/** @hide */
public abstract class HwBinder implements IHwBinder {
private static final String TAG = "HwBinder";
@@ -46,9 +46,16 @@ public abstract class HwBinder implements IHwBinder {
public native final void registerService(String serviceName)
throws RemoteException;
- public static native final IHwBinder getService(
+ public static final IHwBinder getService(
String iface,
String serviceName)
+ throws RemoteException, NoSuchElementException {
+ return getService(iface, serviceName, false /* retry */);
+ }
+ public static native final IHwBinder getService(
+ String iface,
+ String serviceName,
+ boolean retry)
throws RemoteException, NoSuchElementException;
public static native final void configureRpcThreadpool(
@@ -56,6 +63,13 @@ public abstract class HwBinder implements IHwBinder {
public static native final void joinRpcThreadpool();
+ /**
+ * Call configureRpcThreadpool, then actually spawn
+ * (maxThreads - (callerWillJoin ? 0 : 1)) threads.
+ */
+ public static final native void startRpcThreadPool(
+ long maxThreads, boolean callerWillJoin);
+
// Returns address of the "freeFunction".
private static native final long native_init();
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 88226f0a1665..5e9b9ae3d49a 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -43,6 +43,18 @@ public class HwBlob {
public native final double getDouble(long offset);
public native final String getString(long offset);
+ /**
+ The copyTo... methods copy the blob's data, starting from the given
+ byte offset, into the array. A total of "size" _elements_ are copied.
+ */
+ public native final void copyToBoolArray(long offset, boolean[] array, int size);
+ public native final void copyToInt8Array(long offset, byte[] array, int size);
+ public native final void copyToInt16Array(long offset, short[] array, int size);
+ public native final void copyToInt32Array(long offset, int[] array, int size);
+ public native final void copyToInt64Array(long offset, long[] array, int size);
+ public native final void copyToFloatArray(long offset, float[] array, int size);
+ public native final void copyToDoubleArray(long offset, double[] array, int size);
+
public native final void putBool(long offset, boolean x);
public native final void putInt8(long offset, byte x);
public native final void putInt16(long offset, short x);
@@ -52,6 +64,14 @@ public class HwBlob {
public native final void putDouble(long offset, double x);
public native final void putString(long offset, String x);
+ public native final void putBoolArray(long offset, boolean[] x);
+ public native final void putInt8Array(long offset, byte[] x);
+ public native final void putInt16Array(long offset, short[] x);
+ public native final void putInt32Array(long offset, int[] x);
+ public native final void putInt64Array(long offset, long[] x);
+ public native final void putFloatArray(long offset, float[] x);
+ public native final void putDoubleArray(long offset, double[] x);
+
public native final void putBlob(long offset, HwBlob blob);
public native final long handle();
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index 2f89ce6270be..a07e42c720c5 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -63,4 +63,9 @@ public class HwRemoteBinder implements IHwBinder {
}
private long mNativeContext;
+
+ @Override
+ public final native boolean equals(Object other);
+ @Override
+ public final native int hashCode();
}
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 20f6c8ecd495..c0a95cc08dec 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -46,10 +46,10 @@ interface IStatsCompanionService {
* Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
* and alarm is inexact.
*/
- oneway void setPollingAlarms(long timestampMs, long intervalMs);
+ oneway void setPullingAlarms(long timestampMs, long intervalMs);
/** Cancel any repeating polling alarm. */
- oneway void cancelPollingAlarms();
+ oneway void cancelPullingAlarms();
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 2dc3bebb2d10..ca9cbec99cde 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -295,7 +295,11 @@ public final class LocaleList implements Parcelable {
return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale);
}
- private static boolean isPseudoLocale(Locale locale) {
+ /**
+ * Returns true if locale is a pseudo-locale, false otherwise.
+ * {@hide}
+ */
+ public static boolean isPseudoLocale(Locale locale) {
return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
}
diff --git a/core/java/android/os/ShellCallback.java b/core/java/android/os/ShellCallback.java
index e7fe697f9c54..ad9fbfbfae40 100644
--- a/core/java/android/os/ShellCallback.java
+++ b/core/java/android/os/ShellCallback.java
@@ -35,8 +35,9 @@ public class ShellCallback implements Parcelable {
IShellCallback mShellCallback;
class MyShellCallback extends IShellCallback.Stub {
- public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) {
- return onOpenOutputFile(path, seLinuxContext);
+ public ParcelFileDescriptor openFile(String path, String seLinuxContext,
+ String mode) {
+ return onOpenFile(path, seLinuxContext, mode);
}
}
@@ -48,23 +49,27 @@ public class ShellCallback implements Parcelable {
}
/**
- * Ask the shell to open a file for writing. This will truncate the file if it
- * already exists. It will create the file if it doesn't exist.
+ * Ask the shell to open a file. If opening for writing, will truncate the file if it
+ * already exists and will create the file if it doesn't exist.
* @param path Path of the file to be opened/created.
* @param seLinuxContext Optional SELinux context that must be allowed to have
* access to the file; if null, nothing is required.
+ * @param mode Mode to open file in: "r" for input/reading an existing file,
+ * "r+" for reading/writing an existing file, "w" for output/writing a new file (either
+ * creating or truncating an existing one), "w+" for reading/writing a new file (either
+ * creating or truncating an existing one).
*/
- public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) {
- if (DEBUG) Log.d(TAG, "openOutputFile " + this + ": mLocal=" + mLocal
+ public ParcelFileDescriptor openFile(String path, String seLinuxContext, String mode) {
+ if (DEBUG) Log.d(TAG, "openFile " + this + " mode=" + mode + ": mLocal=" + mLocal
+ " mShellCallback=" + mShellCallback);
if (mLocal) {
- return onOpenOutputFile(path, seLinuxContext);
+ return onOpenFile(path, seLinuxContext, mode);
}
if (mShellCallback != null) {
try {
- return mShellCallback.openOutputFile(path, seLinuxContext);
+ return mShellCallback.openFile(path, seLinuxContext, mode);
} catch (RemoteException e) {
Log.w(TAG, "Failure opening " + path, e);
}
@@ -72,7 +77,7 @@ public class ShellCallback implements Parcelable {
return null;
}
- public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext, String mode) {
return null;
}
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 6223235e628f..d75219fdfd11 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -226,10 +226,10 @@ public abstract class ShellCommand {
* Helper for just system services to ask the shell to open an output file.
* @hide
*/
- public ParcelFileDescriptor openOutputFileForSystem(String path) {
+ public ParcelFileDescriptor openFileForSystem(String path, String mode) {
try {
- ParcelFileDescriptor pfd = getShellCallback().openOutputFile(path,
- "u:r:system_server:s0");
+ ParcelFileDescriptor pfd = getShellCallback().openFile(path,
+ "u:r:system_server:s0", mode);
if (pfd != null) {
return pfd;
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index ee3e5bc9a001..6ce12c161714 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2339,7 +2339,8 @@ public final class StrictMode {
/** Stack and violation details. */
@Nullable private final Throwable mThrowable;
- private final Deque<Throwable> mBinderStack = new ArrayDeque<>();
+ /** Path leading to a violation that occurred across binder. */
+ private final Deque<StackTraceElement[]> mBinderStack = new ArrayDeque<>();
/** Memoized stack trace of full violation. */
@Nullable private String mStackTrace;
@@ -2421,9 +2422,13 @@ public final class StrictMode {
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 256);
mThrowable.printStackTrace(pw);
- for (Throwable t : mBinderStack) {
+ for (StackTraceElement[] traces : mBinderStack) {
pw.append("# via Binder call with stack:\n");
- t.printStackTrace(pw);
+ for (StackTraceElement traceElement : traces) {
+ pw.append("\tat ");
+ pw.append(traceElement.toString());
+ pw.append('\n');
+ }
}
pw.flush();
pw.close();
@@ -2456,12 +2461,13 @@ public final class StrictMode {
}
/**
- * Add a {@link Throwable} from the current process that caused the underlying violation.
+ * Add a {@link Throwable} from the current process that caused the underlying violation. We
+ * only preserve the stack trace elements.
*
* @hide
*/
void addLocalStack(Throwable t) {
- mBinderStack.addFirst(t);
+ mBinderStack.addFirst(t.getStackTrace());
}
/**
@@ -2530,7 +2536,17 @@ public final class StrictMode {
mThrowable = (Throwable) in.readSerializable();
int binderStackSize = in.readInt();
for (int i = 0; i < binderStackSize; i++) {
- mBinderStack.add((Throwable) in.readSerializable());
+ StackTraceElement[] traceElements = new StackTraceElement[in.readInt()];
+ for (int j = 0; j < traceElements.length; j++) {
+ StackTraceElement element =
+ new StackTraceElement(
+ in.readString(),
+ in.readString(),
+ in.readString(),
+ in.readInt());
+ traceElements[j] = element;
+ }
+ mBinderStack.add(traceElements);
}
int rawPolicy = in.readInt();
if (unsetGatheringBit) {
@@ -2552,8 +2568,14 @@ public final class StrictMode {
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(mThrowable);
dest.writeInt(mBinderStack.size());
- for (Throwable t : mBinderStack) {
- dest.writeSerializable(t);
+ for (StackTraceElement[] traceElements : mBinderStack) {
+ dest.writeInt(traceElements.length);
+ for (StackTraceElement element : traceElements) {
+ dest.writeString(element.getClassName());
+ dest.writeString(element.getMethodName());
+ dest.writeString(element.getFileName());
+ dest.writeInt(element.getLineNumber());
+ }
}
int start = dest.dataPosition();
dest.writeInt(policy);
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
index 9b3a2d689693..00333dad1a17 100644
--- a/core/java/android/os/TokenWatcher.java
+++ b/core/java/android/os/TokenWatcher.java
@@ -16,17 +16,23 @@
package android.os;
+import android.util.Log;
+
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.WeakHashMap;
import java.util.Set;
-import android.util.Log;
+import java.util.WeakHashMap;
/**
- * Helper class that helps you use IBinder objects as reference counted
- * tokens. IBinders make good tokens because we find out when they are
- * removed
+ * A TokenWatcher watches a collection of {@link IBinder}s. IBinders are added
+ * to the collection by calling {@link #acquire}, and removed by calling {@link
+ * #release}. IBinders are also implicitly removed when they become weakly
+ * reachable. Each IBinder may be added at most once.
*
+ * The {@link #acquired} method is invoked by posting to the specified handler
+ * whenever the size of the watched collection becomes nonzero. The {@link
+ * #released} method is invoked on the specified handler whenever the size of
+ * the watched collection becomes zero.
*/
public abstract class TokenWatcher
{
@@ -59,15 +65,23 @@ public abstract class TokenWatcher
* Record that this token has been acquired. When acquire is called, and
* the current count is 0, the acquired method is called on the given
* handler.
- *
- * @param token An IBinder object. If this token has already been acquired,
- * no action is taken.
+ *
+ * Note that the same {@code token} can only be acquired once. If this
+ * {@code token} has already been acquired, no action is taken. The first
+ * subsequent call to {@link #release} will release this {@code token}
+ * immediately.
+ *
+ * @param token An IBinder object.
* @param tag A string used by the {@link #dump} method for debugging,
* to see who has references.
*/
public void acquire(IBinder token, String tag)
{
synchronized (mTokens) {
+ if (mTokens.containsKey(token)) {
+ return;
+ }
+
// explicitly checked to avoid bogus sendNotification calls because
// of the WeakHashMap and the GC
int oldSize = mTokens.size();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 62f4bf5833d3..2d6a7b0133aa 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -72,6 +72,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.MemoryIntArray;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
@@ -1886,7 +1887,11 @@ public final class Settings {
arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
+ String prevValue = getStringForUser(cr, name, userHandle);
cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
+ String newValue = getStringForUser(cr, name, userHandle);
+ StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
+ makeDefault ? 1 : 0, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 625dd9ebfef8..cd177c42d6b3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -16,6 +16,8 @@
package android.service.voice;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
import android.annotation.Nullable;
import android.app.Activity;
import android.app.Dialog;
@@ -46,7 +48,6 @@ import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -63,8 +64,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
/**
* An active voice interaction session, providing a facility for the implementation
* to interact with the user in the voice interaction layer. The user interface is
@@ -110,16 +109,6 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
*/
public static final int SHOW_SOURCE_ACTIVITY = 1<<4;
- // Keys for Bundle values
- /** @hide */
- public static final String KEY_DATA = "data";
- /** @hide */
- public static final String KEY_STRUCTURE = "structure";
- /** @hide */
- public static final String KEY_CONTENT = "content";
- /** @hide */
- public static final String KEY_RECEIVER_EXTRAS = "receiverExtras";
-
final Context mContext;
final HandlerCaller mHandlerCaller;
@@ -1423,9 +1412,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
public void setContentView(View view) {
ensureWindowCreated();
mContentFrame.removeAllViews();
- mContentFrame.addView(view, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
+ mContentFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentFrame.requestApplyInsets();
}
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index fef92230e7b8..7285fb40ae02 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -101,5 +101,13 @@ interface IVrManager {
* application's compositor process to bind to, or null to clear the current binding.
*/
void setAndBindCompositor(in String componentName);
+
+ /**
+ * Sets the current standby status of the VR device. Standby mode is only used on standalone vr
+ * devices. Standby mode is a deep sleep state where it's appropriate to turn off vr mode.
+ *
+ * @param standy True if the device is entering standby, false if it's exiting standby.
+ */
+ void setStandbyEnabled(boolean standby);
}
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 43a978974fe3..a94806a02fbb 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -127,42 +127,48 @@ public final class ProtoOutputStream {
public static final long FIELD_TYPE_UNKNOWN = 0;
+ /**
+ * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly,
+ * so no extra mapping needs to be maintained in this case.
+ */
public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_INT32 = 3L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_INT64 = 4L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_UINT32 = 5L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_UINT64 = 6L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SINT32 = 7L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SINT64 = 8L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_FIXED32 = 9L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_FIXED64 = 10L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SFIXED32 = 11L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_SFIXED64 = 12L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_BOOL = 13L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_STRING = 14L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_BYTES = 15L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_ENUM = 16L << FIELD_TYPE_SHIFT;
- public static final long FIELD_TYPE_OBJECT = 17L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT;
+// public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated.
+ public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT;
+ public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT;
private static final String[] FIELD_TYPE_NAMES = new String[] {
"Double",
"Float",
- "Int32",
"Int64",
- "UInt32",
"UInt64",
- "SInt32",
- "SInt64",
- "Fixed32",
+ "Int32",
"Fixed64",
- "SFixed32",
- "SFixed64",
+ "Fixed32",
"Bool",
"String",
+ "Group", // This field is deprecated but reserved here for indexing.
+ "Message",
"Bytes",
+ "UInt32",
"Enum",
- "Object",
+ "SFixed32",
+ "SFixed64",
+ "SInt32",
+ "SInt64",
};
//
@@ -867,21 +873,21 @@ public final class ProtoOutputStream {
assertNotCompacted();
final int id = (int)fieldId;
- switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
+ switch ((int) ((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
// bytes
- case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+ case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
writeBytesImpl(id, val);
break;
- case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
- case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+ case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+ case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
writeRepeatedBytesImpl(id, val);
break;
// Object
- case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
+ case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
writeObjectImpl(id, val);
break;
- case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
- case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
+ case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
+ case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
writeRepeatedObjectImpl(id, val);
break;
// nothing else allowed
@@ -899,7 +905,7 @@ public final class ProtoOutputStream {
assertNotCompacted();
final int id = (int)fieldId;
- if ((fieldId & FIELD_TYPE_MASK) == FIELD_TYPE_OBJECT) {
+ if ((fieldId & FIELD_TYPE_MASK) == FIELD_TYPE_MESSAGE) {
final long count = fieldId & FIELD_COUNT_MASK;
if (count == FIELD_COUNT_SINGLE) {
return startObjectImpl(id, false);
@@ -2091,7 +2097,7 @@ public final class ProtoOutputStream {
@Deprecated
public long startObject(long fieldId) {
assertNotCompacted();
- final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_OBJECT);
+ final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE);
return startObjectImpl(id, false);
}
@@ -2119,7 +2125,7 @@ public final class ProtoOutputStream {
@Deprecated
public long startRepeatedObject(long fieldId) {
assertNotCompacted();
- final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_OBJECT);
+ final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE);
return startObjectImpl(id, true);
}
@@ -2217,7 +2223,7 @@ public final class ProtoOutputStream {
@Deprecated
public void writeObject(long fieldId, byte[] value) {
assertNotCompacted();
- final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_OBJECT);
+ final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE);
writeObjectImpl(id, value);
}
@@ -2237,7 +2243,7 @@ public final class ProtoOutputStream {
@Deprecated
public void writeRepeatedObject(long fieldId, byte[] value) {
assertNotCompacted();
- final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_OBJECT);
+ final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE);
writeRepeatedObjectImpl(id, value);
}
@@ -2296,7 +2302,7 @@ public final class ProtoOutputStream {
final String typeString = getFieldTypeString(fieldType);
if (typeString != null && countString != null) {
final StringBuilder sb = new StringBuilder();
- if (expectedType == FIELD_TYPE_OBJECT) {
+ if (expectedType == FIELD_TYPE_MESSAGE) {
sb.append("start");
} else {
sb.append("write");
@@ -2306,7 +2312,7 @@ public final class ProtoOutputStream {
sb.append(" called for field ");
sb.append((int)fieldId);
sb.append(" which should be used with ");
- if (fieldType == FIELD_TYPE_OBJECT) {
+ if (fieldType == FIELD_TYPE_MESSAGE) {
sb.append("start");
} else {
sb.append("write");
@@ -2321,7 +2327,7 @@ public final class ProtoOutputStream {
throw new IllegalArgumentException(sb.toString());
} else {
final StringBuilder sb = new StringBuilder();
- if (expectedType == FIELD_TYPE_OBJECT) {
+ if (expectedType == FIELD_TYPE_MESSAGE) {
sb.append("start");
} else {
sb.append("write");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6e49bac38116..65bde49520d3 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -16,7 +16,7 @@
package android.view;
-import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.os.IResultReceiver;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -272,7 +272,7 @@ interface IWindowManager
/**
* Used only for assist -- request a screenshot of the current application.
*/
- boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver);
+ boolean requestAssistScreenshot(IAssistDataReceiver receiver);
/**
* Called by the status bar to notify Views of changes to System UI visiblity.
@@ -385,7 +385,7 @@ interface IWindowManager
/**
* Create an input consumer by name.
*/
- void createInputConsumer(String name, out InputChannel inputChannel);
+ void createInputConsumer(IBinder token, String name, out InputChannel inputChannel);
/**
* Destroy an input consumer by name. This method will also dispose the input channels
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 580456023d68..ab0b3eec8753 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -43,6 +44,7 @@ public class NotificationHeaderView extends ViewGroup {
public static final int NO_COLOR = Notification.COLOR_INVALID;
private final int mChildMinWidth;
private final int mContentEndMargin;
+ private final int mGravity;
private View mAppName;
private View mHeaderText;
private OnClickListener mExpandClickListener;
@@ -50,7 +52,6 @@ public class NotificationHeaderView extends ViewGroup {
private ImageView mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
- private View mInfo;
private int mIconColor;
private int mOriginalNotificationColor;
private boolean mExpanded;
@@ -61,6 +62,7 @@ public class NotificationHeaderView extends ViewGroup {
private boolean mEntireHeaderClickable;
private boolean mExpandOnlyOnButton;
private boolean mAcceptAllTouches;
+ private int mTotalWidth;
ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
@@ -92,6 +94,11 @@ public class NotificationHeaderView extends ViewGroup {
mHeaderBackgroundHeight = res.getDimensionPixelSize(
R.dimen.notification_header_background_height);
mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);
+
+ int[] attrIds = { android.R.attr.gravity };
+ TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
+ mGravity = ta.getInt(0, 0);
+ ta.recycle();
}
@Override
@@ -146,6 +153,7 @@ public class NotificationHeaderView extends ViewGroup {
mHeaderText.measure(childWidthSpec, wrapContentHeightSpec);
}
}
+ mTotalWidth = Math.min(totalWidth, givenWidth);
setMeasuredDimension(givenWidth, givenHeight);
}
@@ -153,6 +161,10 @@ public class NotificationHeaderView extends ViewGroup {
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = getPaddingStart();
int end = getMeasuredWidth();
+ final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
+ if (centerAligned) {
+ left += getMeasuredWidth() / 2 - mTotalWidth / 2;
+ }
int childCount = getChildCount();
int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
for (int i = 0; i < childCount; i++) {
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 45008627e019..c44c8dda83a9 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -92,7 +92,7 @@ public class ViewConfiguration {
* Defines the duration in milliseconds a user needs to hold down the
* appropriate button to enable the accessibility shortcut once it's configured.
*/
- private static final int A11Y_SHORTCUT_KEY_TIMEOUT_AFTER_CONFIRMATION = 1500;
+ private static final int A11Y_SHORTCUT_KEY_TIMEOUT_AFTER_CONFIRMATION = 1000;
/**
* Defines the duration in milliseconds we will wait to see if a touch event
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 19213ca06c5e..c3d6c695982d 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -187,8 +187,11 @@ public final class AccessibilityInteractionClient
Log.i(LOG_TAG, "Window cache miss");
}
final long identityToken = Binder.clearCallingIdentity();
- window = connection.getWindow(accessibilityWindowId);
- Binder.restoreCallingIdentity(identityToken);
+ try {
+ window = connection.getWindow(accessibilityWindowId);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
if (window != null) {
sAccessibilityCache.addWindow(window);
return window;
@@ -225,8 +228,11 @@ public final class AccessibilityInteractionClient
Log.i(LOG_TAG, "Windows cache miss");
}
final long identityToken = Binder.clearCallingIdentity();
- windows = connection.getWindows();
- Binder.restoreCallingIdentity(identityToken);
+ try {
+ windows = connection.getWindows();
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
if (windows != null) {
sAccessibilityCache.setWindows(windows);
return windows;
@@ -283,10 +289,14 @@ public final class AccessibilityInteractionClient
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
- accessibilityWindowId, accessibilityNodeId, interactionId, this,
- prefetchFlags, Thread.currentThread().getId(), arguments);
- Binder.restoreCallingIdentity(identityToken);
+ final boolean success;
+ try {
+ success = connection.findAccessibilityNodeInfoByAccessibilityId(
+ accessibilityWindowId, accessibilityNodeId, interactionId, this,
+ prefetchFlags, Thread.currentThread().getId(), arguments);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
@@ -333,10 +343,15 @@ public final class AccessibilityInteractionClient
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success = connection.findAccessibilityNodeInfosByViewId(
- accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
- Thread.currentThread().getId());
- Binder.restoreCallingIdentity(identityToken);
+ final boolean success;
+ try {
+ success = connection.findAccessibilityNodeInfosByViewId(
+ accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
+ Thread.currentThread().getId());
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
@@ -381,10 +396,15 @@ public final class AccessibilityInteractionClient
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success = connection.findAccessibilityNodeInfosByText(
- accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
- Thread.currentThread().getId());
- Binder.restoreCallingIdentity(identityToken);
+ final boolean success;
+ try {
+ success = connection.findAccessibilityNodeInfosByText(
+ accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
+ Thread.currentThread().getId());
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
@@ -428,10 +448,15 @@ public final class AccessibilityInteractionClient
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success = connection.findFocus(accessibilityWindowId,
- accessibilityNodeId, focusType, interactionId, this,
- Thread.currentThread().getId());
- Binder.restoreCallingIdentity(identityToken);
+ final boolean success;
+ try {
+ success = connection.findFocus(accessibilityWindowId,
+ accessibilityNodeId, focusType, interactionId, this,
+ Thread.currentThread().getId());
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
@@ -472,10 +497,15 @@ public final class AccessibilityInteractionClient
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success = connection.focusSearch(accessibilityWindowId,
- accessibilityNodeId, direction, interactionId, this,
- Thread.currentThread().getId());
- Binder.restoreCallingIdentity(identityToken);
+ final boolean success;
+ try {
+ success = connection.focusSearch(accessibilityWindowId,
+ accessibilityNodeId, direction, interactionId, this,
+ Thread.currentThread().getId());
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
@@ -515,10 +545,15 @@ public final class AccessibilityInteractionClient
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success = connection.performAccessibilityAction(
- accessibilityWindowId, accessibilityNodeId, action, arguments,
- interactionId, this, Thread.currentThread().getId());
- Binder.restoreCallingIdentity(identityToken);
+ final boolean success;
+ try {
+ success = connection.performAccessibilityAction(
+ accessibilityWindowId, accessibilityNodeId, action, arguments,
+ interactionId, this, Thread.currentThread().getId());
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+
if (success) {
return getPerformAccessibilityActionResultAndClear(interactionId);
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 0b9bc5760fa8..35f6acba04dc 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -436,8 +436,11 @@ public final class AccessibilityManager {
// client using it is called through Binder from another process. Example: MMS
// app adds a SMS notification and the NotificationManagerService calls this method
long identityToken = Binder.clearCallingIdentity();
- service.sendAccessibilityEvent(event, userId);
- Binder.restoreCallingIdentity(identityToken);
+ try {
+ service.sendAccessibilityEvent(event, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
if (DEBUG) {
Log.i(LOG_TAG, event + " sent");
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e79d201be2c5..6a0669b022f7 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1272,18 +1272,32 @@ public final class AutofillManager {
}
}
- private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
+ /** @hide */
+ public static final int SET_STATE_FLAG_ENABLED = 0x01;
+ /** @hide */
+ public static final int SET_STATE_FLAG_RESET_SESSION = 0x02;
+ /** @hide */
+ public static final int SET_STATE_FLAG_RESET_CLIENT = 0x04;
+ /** @hide */
+ public static final int SET_STATE_FLAG_DEBUG = 0x08;
+ /** @hide */
+ public static final int SET_STATE_FLAG_VERBOSE = 0x10;
+
+ private void setState(int flags) {
+ if (sVerbose) Log.v(TAG, "setState(" + flags + ")");
synchronized (mLock) {
- mEnabled = enabled;
- if (!mEnabled || resetSession) {
+ mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
+ if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
// Reset the session state
resetSessionLocked();
}
- if (resetClient) {
+ if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
// Reset connection to system
mServiceClient = null;
}
}
+ sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
+ sVerbose = (flags & SET_STATE_FLAG_VERBOSE) != 0;
}
/**
@@ -1609,6 +1623,7 @@ public final class AutofillManager {
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
pw.print(pfx); pw.print("context: "); pw.println(mContext);
+ pw.print(pfx); pw.print("client: "); pw.println(getClientLocked());
pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
@@ -1625,6 +1640,8 @@ public final class AutofillManager {
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
+ pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
+ pw.print(" verbose: "); pw.println(sVerbose);
}
private String getStateAsStringLocked() {
@@ -1940,10 +1957,10 @@ public final class AutofillManager {
}
@Override
- public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
+ public void setState(int flags) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.setState(enabled, resetSession, resetClient));
+ afm.post(() -> afm.setState(flags));
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 368c02907bf3..254c8a5ac20c 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -35,7 +35,7 @@ oneway interface IAutoFillManagerClient {
/**
* Notifies the client when the autofill enabled state changed.
*/
- void setState(boolean enabled, boolean resetSession, boolean resetClient);
+ void setState(int flags);
/**
* Autofills the activity with the contents of a dataset.
diff --git a/core/java/android/webkit/ServiceWorkerClient.java b/core/java/android/webkit/ServiceWorkerClient.java
index d6e8f36ce101..9124c8554a26 100644
--- a/core/java/android/webkit/ServiceWorkerClient.java
+++ b/core/java/android/webkit/ServiceWorkerClient.java
@@ -29,9 +29,9 @@ public class ServiceWorkerClient {
* application to return the data. If the return value is {@code null}, the
* Service Worker will continue to load the resource as usual.
* Otherwise, the return response and data will be used.
- * NOTE: This method is called on a thread other than the UI thread
- * so clients should exercise caution when accessing private data
- * or the view system.
+ *
+ * <p class="note"><b>Note:</b> This method is called on a thread other than the UI thread so
+ * clients should exercise caution when accessing private data or the view system.
*
* @param request Object containing the details of the request.
* @return A {@link android.webkit.WebResourceResponse} containing the
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 259bf60a3059..9295f5cb228a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -191,7 +191,7 @@ import java.util.Map;
* {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
* (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
*
- * <p>NOTE: Using zoom if either the height or width is set to
+ * <p class="note"><b>Note:</b> Using zoom if either the height or width is set to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior
* and should be avoided.
*
@@ -536,9 +536,13 @@ public class WebView extends AbsoluteLayout
}
/**
- * Constructs a new WebView with a Context object.
+ * Constructs a new WebView with an Activity Context object.
*
- * @param context a Context object used to access application assets
+ * <p class="note"><b>Note:</b> WebView should always be instantiated with an Activity Context.
+ * If instantiated with an Application Context, WebView will be unable to provide several
+ * features, such as JavaScript dialogs and autofill.
+ *
+ * @param context an Activity Context to access application assets
*/
public WebView(Context context) {
this(context, null);
@@ -547,7 +551,7 @@ public class WebView extends AbsoluteLayout
/**
* Constructs a new WebView with layout parameters.
*
- * @param context a Context object used to access application assets
+ * @param context an Activity Context to access application assets
* @param attrs an AttributeSet passed to our parent
*/
public WebView(Context context, AttributeSet attrs) {
@@ -557,7 +561,7 @@ public class WebView extends AbsoluteLayout
/**
* Constructs a new WebView with layout parameters and a default style.
*
- * @param context a Context object used to access application assets
+ * @param context an Activity Context to access application assets
* @param attrs an AttributeSet passed to our parent
* @param defStyleAttr an attribute in the current theme that contains a
* reference to a style resource that supplies default values for
@@ -570,7 +574,7 @@ public class WebView extends AbsoluteLayout
/**
* Constructs a new WebView with layout parameters and a default style.
*
- * @param context a Context object used to access application assets
+ * @param context an Activity Context to access application assets
* @param attrs an AttributeSet passed to our parent
* @param defStyleAttr an attribute in the current theme that contains a
* reference to a style resource that supplies default values for
@@ -587,7 +591,7 @@ public class WebView extends AbsoluteLayout
/**
* Constructs a new WebView with layout parameters and a default style.
*
- * @param context a Context object used to access application assets
+ * @param context an Activity Context to access application assets
* @param attrs an AttributeSet passed to our parent
* @param defStyleAttr an attribute in the current theme that contains a
* reference to a style resource that supplies default values for
@@ -612,7 +616,7 @@ public class WebView extends AbsoluteLayout
* time. This guarantees that these interfaces will be available when the JS
* context is initialized.
*
- * @param context a Context object used to access application assets
+ * @param context an Activity Context to access application assets
* @param attrs an AttributeSet passed to our parent
* @param defStyleAttr an attribute in the current theme that contains a
* reference to a style resource that supplies default values for
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index c5b64eb89f44..517ad07c8323 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -130,7 +130,7 @@ public class WebViewClient {
* <p>This method is called when the body of the HTTP response has started loading, is reflected
* in the DOM, and will be visible in subsequent draws. This callback occurs early in the
* document loading process, and as such you should expect that linked resources (for example,
- * css and images) may not be available.
+ * CSS and images) may not be available.
*
* <p>For more fine-grained notification of visual state updates, see {@link
* WebView#postVisualStateCallback}.
@@ -150,13 +150,15 @@ public class WebViewClient {
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is {@code null}, the WebView
* will continue to load the resource as usual. Otherwise, the return
- * response and data will be used. NOTE: This method is called on a thread
+ * response and data will be used.
+ *
+ * <p class="note"><b>Note:</b> This method is called on a thread
* other than the UI thread so clients should exercise caution
* when accessing private data or the view system.
*
- * <p>Note: when Safe Browsing is enabled, these URLs still undergo Safe Browsing checks. If
- * this is undesired, whitelist the URL with {@link WebView#setSafeBrowsingWhitelist} or ignore
- * the warning with {@link #onSafeBrowsingHit}.
+ * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
+ * Browsing checks. If this is undesired, whitelist the URL with {@link
+ * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
@@ -178,13 +180,15 @@ public class WebViewClient {
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is {@code null}, the WebView
* will continue to load the resource as usual. Otherwise, the return
- * response and data will be used. NOTE: This method is called on a thread
+ * response and data will be used.
+ *
+ * <p class="note"><b>Note:</b> This method is called on a thread
* other than the UI thread so clients should exercise caution
* when accessing private data or the view system.
*
- * <p>Note: when Safe Browsing is enabled, these URLs still undergo Safe Browsing checks. If
- * this is undesired, whitelist the URL with {@link WebView#setSafeBrowsingWhitelist} or ignore
- * the warning with {@link #onSafeBrowsingHit}.
+ * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
+ * Browsing checks. If this is undesired, whitelist the URL with {@link
+ * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
@@ -248,7 +252,7 @@ public class WebViewClient {
public static final int ERROR_FILE_NOT_FOUND = -14;
/** Too many requests during this load */
public static final int ERROR_TOO_MANY_REQUESTS = -15;
- /** Resource load was cancelled by Safe Browsing */
+ /** Resource load was canceled by Safe Browsing */
public static final int ERROR_UNSAFE_RESOURCE = -16;
/** @hide */
@@ -272,8 +276,8 @@ public class WebViewClient {
/**
* Report an error to the host application. These errors are unrecoverable
- * (i.e. the main resource is unavailable). The errorCode parameter
- * corresponds to one of the ERROR_* constants.
+ * (i.e. the main resource is unavailable). The {@code errorCode} parameter
+ * corresponds to one of the {@code ERROR_*} constants.
* @param view The WebView that is initiating the callback.
* @param errorCode The error code corresponding to an ERROR_* value.
* @param description A String describing the error.
@@ -289,11 +293,11 @@ public class WebViewClient {
/**
* Report web resource loading error to the host application. These errors usually indicate
* inability to connect to the server. Note that unlike the deprecated version of the callback,
- * the new version will be called for any resource (iframe, image, etc), not just for the main
+ * the new version will be called for any resource (iframe, image, etc.), not just for the main
* page. Thus, it is recommended to perform minimum required work in this callback.
* @param view The WebView that is initiating the callback.
* @param request The originating request.
- * @param error Information about the error occured.
+ * @param error Information about the error occurred.
*/
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
if (request.isForMainFrame()) {
@@ -306,12 +310,12 @@ public class WebViewClient {
/**
* Notify the host application that an HTTP error has been received from the server while
* loading a resource. HTTP errors have status codes &gt;= 400. This callback will be called
- * for any resource (iframe, image, etc), not just for the main page. Thus, it is recommended to
- * perform minimum required work in this callback. Note that the content of the server
- * response may not be provided within the <b>errorResponse</b> parameter.
+ * for any resource (iframe, image, etc.), not just for the main page. Thus, it is recommended
+ * to perform minimum required work in this callback. Note that the content of the server
+ * response may not be provided within the {@code errorResponse} parameter.
* @param view The WebView that is initiating the callback.
* @param request The originating request.
- * @param errorResponse Information about the error occured.
+ * @param errorResponse Information about the error occurred.
*/
public void onReceivedHttpError(
WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
@@ -365,7 +369,7 @@ public class WebViewClient {
* if desired and providing the keys. There are three ways to
* respond: proceed(), cancel() or ignore(). Webview stores the response
* in memory (for the life of the application) if proceed() or cancel() is
- * called and does not call onReceivedClientCertRequest() again for the
+ * called and does not call {@code onReceivedClientCertRequest()} again for the
* same host and port pair. Webview does not store the response if ignore()
* is called. Note that, multiple layers in chromium network stack might be
* caching the responses, so the behavior for ignore is only a best case
@@ -432,7 +436,7 @@ public class WebViewClient {
/**
* Notify the host application that a key was not handled by the WebView.
* Except system keys, WebView always consumes the keys in the normal flow
- * or if shouldOverrideKeyEvent returns {@code true}. This is called asynchronously
+ * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously
* from where the key is dispatched. It gives the host application a chance
* to handle the unhandled key events.
*
@@ -446,7 +450,7 @@ public class WebViewClient {
/**
* Notify the host application that a input event was not handled by the WebView.
* Except system keys, WebView always consumes input events in the normal flow
- * or if shouldOverrideKeyEvent returns {@code true}. This is called asynchronously
+ * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously
* from where the event is dispatched. It gives the host application a chance
* to handle the unhandled input events.
*
@@ -503,7 +507,7 @@ public class WebViewClient {
}
/**
- * Notify host application that the given webview's render process has exited.
+ * Notify host application that the given WebView's render process has exited.
*
* Multiple WebView instances may be associated with a single render process;
* onRenderProcessGone will be called for each WebView that was affected.
@@ -513,10 +517,10 @@ public class WebViewClient {
*
* The given WebView can't be used, and should be removed from the view hierarchy,
* all references to it should be cleaned up, e.g any references in the Activity
- * or other classes saved using findViewById and similar calls, etc
+ * or other classes saved using {@link android.view.View#findViewById} and similar calls, etc.
*
* To cause an render process crash for test purpose, the application can
- * call loadUrl("chrome://crash") on the WebView. Note that multiple WebView
+ * call {@code loadUrl("chrome://crash")} on the WebView. Note that multiple WebView
* instances may be affected if they share a render process, not just the
* specific WebView which loaded chrome://crash.
*
@@ -537,12 +541,13 @@ public class WebViewClient {
* behavior is to show an interstitial to the user, with the reporting checkbox visible.
*
* If the application needs to show its own custom interstitial UI, the callback can be invoked
- * asynchronously with backToSafety() or proceed(), depending on user response.
+ * asynchronously with {@link SafeBrowsingResponse#backToSafety} or {@link
+ * SafeBrowsingResponse#proceed}, depending on user response.
*
* @param view The WebView that hit the malicious resource.
* @param request Object containing the details of the request.
* @param threatType The reason the resource was caught by Safe Browsing, corresponding to a
- * SAFE_BROWSING_THREAT_* value.
+ * {@code SAFE_BROWSING_THREAT_*} value.
* @param callback Applications must invoke one of the callback methods.
*/
public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index 341c69fd2eba..de0b97d15e23 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -229,7 +229,9 @@ public class WebViewLibraryLoader {
/**
* Load WebView's native library into the current process.
- * Note: assumes that we have waited for relro creation.
+ *
+ * <p class="note"><b>Note:</b> Assumes that we have waited for relro creation.
+ *
* @param clazzLoader class loader used to find the linker namespace to load the library into.
* @param packageInfo the package from which WebView is loaded.
*/
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index c46c681c3285..a8969252ff2e 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -316,7 +316,7 @@ public interface WebViewProvider {
/**
* Provides mechanism for the name-sake methods declared in View and ViewGroup to be delegated
* into the WebViewProvider instance.
- * NOTE For many of these methods, the WebView will provide a super.Foo() call before or after
+ * NOTE: For many of these methods, the WebView will provide a super.Foo() call before or after
* making the call into the provider instance. This is done for convenience in the common case
* of maintaining backward compatibility. For remaining super class calls (e.g. where the
* provider may need to only conditionally make the call based on some internal state) see the
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index dd01251cf9b9..6c192563658e 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -202,7 +202,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
* The last selected position we used when notifying
*/
int mOldSelectedPosition = INVALID_POSITION;
-
+
/**
* The id of the last selected position we used when notifying
*/
@@ -382,7 +382,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
* position is different from the previously selected position or if
* there was no selected item.</p>
*
- * Impelmenters can call getItemAtPosition(position) if they need to access the
+ * Implementers can call getItemAtPosition(position) if they need to access the
* data associated with the selected item.
*
* @param parent The AdapterView where the selection happened
@@ -778,8 +778,8 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
// We are now GONE, so pending layouts will not be dispatched.
// Force one here to make sure that the state of the list matches
// the state of the adapter.
- if (mDataChanged) {
- this.onLayout(false, mLeft, mTop, mRight, mBottom);
+ if (mDataChanged) {
+ this.onLayout(false, mLeft, mTop, mRight, mBottom);
}
} else {
if (mEmptyView != null) mEmptyView.setVisibility(View.GONE);
@@ -1304,4 +1304,4 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
structure.setAutofillOptions(options);
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/app/IAppOpsCallback.aidl b/core/java/com/android/internal/app/IAppOpsCallback.aidl
index 5fdc92041c3c..15221b1f0fa7 100644
--- a/core/java/com/android/internal/app/IAppOpsCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsCallback.aidl
@@ -17,7 +17,7 @@
package com.android.internal.app;
// This interface is also used by native code, so must
-// be kept in sync with frameworks/native/include/binder/IAppOpsCallback.h
+// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsCallback.h
oneway interface IAppOpsCallback {
void opChanged(int op, int uid, String packageName);
}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 7a119b4351c0..2b975fe03bf2 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -22,7 +22,7 @@ import com.android.internal.app.IAppOpsCallback;
interface IAppOpsService {
// These first methods are also called by native code, so must
- // be kept in sync with frameworks/native/include/binder/IAppOpsService.h
+ // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName);
int startOperation(IBinder token, int code, int uid, String packageName);
diff --git a/core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl b/core/java/com/android/internal/app/IAssistDataReceiver.aidl
index a987a166b496..9c9ffef0066f 100644
--- a/core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl
+++ b/core/java/com/android/internal/app/IAssistDataReceiver.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
@@ -17,8 +17,10 @@
package com.android.internal.app;
import android.graphics.Bitmap;
+import android.os.Bundle;
/** @hide */
-oneway interface IAssistScreenshotReceiver {
- void send(in Bitmap screenshot);
+oneway interface IAssistDataReceiver {
+ void onHandleAssistData(in Bundle resultData);
+ void onHandleAssistScreenshot(in Bitmap screenshot);
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 4275e0b43a4e..f40523162078 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -29,7 +29,7 @@ import android.telephony.SignalStrength;
interface IBatteryStats {
// These first methods are also called by native code, so must
- // be kept in sync with frameworks/native/include/binder/IBatteryStats.h
+ // be kept in sync with frameworks/native/libs/binder/include/binder/IBatteryStats.h
void noteStartSensor(int uid, int sensor);
void noteStopSensor(int uid, int sensor);
void noteStartVideo(int uid);
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index 386aa84bfdbe..0a230a90a735 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -136,7 +136,16 @@ public class LocaleHelper {
* @return the localized country name.
*/
public static String getDisplayCountry(Locale locale, Locale displayLocale) {
- return ULocale.getDisplayCountry(locale.toLanguageTag(), ULocale.forLocale(displayLocale));
+ final String languageTag = locale.toLanguageTag();
+ final ULocale uDisplayLocale = ULocale.forLocale(displayLocale);
+ final String country = ULocale.getDisplayCountry(languageTag, uDisplayLocale);
+ final String numberingSystem = locale.getUnicodeLocaleType("nu");
+ if (numberingSystem != null) {
+ return String.format("%s (%s)", country,
+ ULocale.getDisplayKeywordValue(languageTag, "numbers", uDisplayLocale));
+ } else {
+ return country;
+ }
}
/**
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 9936ed5c6491..c8c2fcf60d1f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -93,10 +93,6 @@ public class LocalePicker extends ListFragment {
return context.getResources().getStringArray(R.array.supported_locales);
}
- public static String[] getPseudoLocales() {
- return pseudoLocales;
- }
-
public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
final Resources resources = context.getResources();
@@ -104,13 +100,6 @@ public class LocalePicker extends ListFragment {
List<String> localeList = new ArrayList<String>(locales.length);
Collections.addAll(localeList, locales);
- // Don't show the pseudolocales unless we're in developer mode. http://b/17190407.
- if (!isInDeveloperMode) {
- for (String locale : pseudoLocales) {
- localeList.remove(locale);
- }
- }
-
Collections.sort(localeList);
final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
@@ -122,6 +111,10 @@ public class LocalePicker extends ListFragment {
|| l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
continue;
}
+ // Don't show the pseudolocales unless we're in developer mode. http://b/17190407.
+ if (!isInDeveloperMode && LocaleList.isPseudoLocale(l)) {
+ continue;
+ }
if (localeInfos.isEmpty()) {
if (DEBUG) {
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index e3fce5197dca..2b0b5eec6c56 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -17,6 +17,7 @@
package com.android.internal.app;
import android.content.Context;
+import android.os.LocaleList;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -68,7 +69,9 @@ public class LocaleStore {
return null;
}
return new Locale.Builder()
- .setLocale(locale).setRegion("")
+ .setLocale(locale)
+ .setRegion("")
+ .setExtension(Locale.UNICODE_LOCALE_EXTENSION, "")
.build();
}
@@ -253,11 +256,25 @@ public class LocaleStore {
Set<String> simCountries = getSimCountries(context);
+ final boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
for (String localeId : LocalePicker.getSupportedLocales(context)) {
if (localeId.isEmpty()) {
throw new IllformedLocaleException("Bad locale entry in locale_config.xml");
}
LocaleInfo li = new LocaleInfo(localeId);
+
+ if (LocaleList.isPseudoLocale(li.getLocale())) {
+ if (isInDeveloperMode) {
+ li.setTranslated(true);
+ li.mIsPseudo = true;
+ li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
+ } else {
+ // Do not display pseudolocales unless in development mode.
+ continue;
+ }
+ }
+
if (simCountries.contains(li.getLocale().getCountry())) {
li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
}
@@ -271,19 +288,6 @@ public class LocaleStore {
}
}
- boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
- for (String localeId : LocalePicker.getPseudoLocales()) {
- LocaleInfo li = getLocaleInfo(Locale.forLanguageTag(localeId));
- if (isInDeveloperMode) {
- li.setTranslated(true);
- li.mIsPseudo = true;
- li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
- } else {
- sLocaleCache.remove(li.getId());
- }
- }
-
// TODO: See if we can reuse what LocaleList.matchScore does
final HashSet<String> localizedLocales = new HashSet<>();
for (String localeId : LocalePicker.getSystemAssetLocales()) {
diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/NightDisplayController.java
index b2053c004e2f..b8bfc6491f70 100644
--- a/core/java/com/android/internal/app/NightDisplayController.java
+++ b/core/java/com/android/internal/app/NightDisplayController.java
@@ -25,6 +25,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemProperties;
import android.provider.Settings.Secure;
import android.provider.Settings.System;
import android.util.Slog;
@@ -100,6 +101,12 @@ public final class NightDisplayController {
*/
public static final int COLOR_MODE_SATURATED = 2;
+ /**
+ * See com.android.server.display.DisplayTransformManager.
+ */
+ private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
+ private static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode";
+
private final Context mContext;
private final int mUserId;
@@ -334,9 +341,15 @@ public final class NightDisplayController {
*/
public int getColorMode() {
final int colorMode = System.getIntForUser(mContext.getContentResolver(),
- System.DISPLAY_COLOR_MODE, COLOR_MODE_BOOSTED, mUserId);
+ System.DISPLAY_COLOR_MODE, -1, mUserId);
if (colorMode < COLOR_MODE_NATURAL || colorMode > COLOR_MODE_SATURATED) {
- return COLOR_MODE_BOOSTED;
+ // There still might be a legacy system property controlling color mode that we need to
+ // respect.
+ if ("1".equals(SystemProperties.get(PERSISTENT_PROPERTY_NATIVE_MODE))) {
+ return COLOR_MODE_SATURATED;
+ }
+ return "1.0".equals(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION))
+ ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
}
return colorMode;
}
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index 7cb32ff793e7..e2064a8099ae 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -2,5 +2,5 @@ set noparent
ek@google.com
hugobenichi@google.com
-jsharkey@google.com
+jsharkey@android.com
lorenzo@google.com
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index 3baccee049b0..05ec9e90513e 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -106,6 +106,14 @@ public abstract class BaseCommand {
}
/**
+ * Peek the next argument on the command line, whatever it is; if there are
+ * no arguments left, return null.
+ */
+ public String peekNextArg() {
+ return mArgs.peekNextArg();
+ }
+
+ /**
* Return the next argument on the command line, whatever it is; if there are
* no arguments left, throws an IllegalArgumentException to report this to the user.
*/
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f0d05da2ec5e..0535ebeb7615 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5412,6 +5412,18 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ public String[] getWifiIfaces() {
+ synchronized (mWifiNetworkLock) {
+ return mWifiIfaces;
+ }
+ }
+
+ public String[] getMobileIfaces() {
+ synchronized (mModemNetworkLock) {
+ return mModemIfaces;
+ }
+ }
+
@Override public long getScreenOnTime(long elapsedRealtimeUs, int which) {
return mScreenOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
diff --git a/core/java/com/android/internal/os/IShellCallback.aidl b/core/java/com/android/internal/os/IShellCallback.aidl
index 57d67890d840..57043424fc47 100644
--- a/core/java/com/android/internal/os/IShellCallback.aidl
+++ b/core/java/com/android/internal/os/IShellCallback.aidl
@@ -20,5 +20,5 @@ import android.os.ParcelFileDescriptor;
/** @hide */
interface IShellCallback {
- ParcelFileDescriptor openOutputFile(String path, String seLinuxContext);
+ ParcelFileDescriptor openFile(String path, String seLinuxContext, String mode);
}
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index fb6b8b0b2e16..3af3e2ad2772 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -16,6 +16,10 @@
package com.android.internal.policy;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -99,11 +103,12 @@ public class DividerSnapAlgorithm {
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
boolean isHorizontalDivision, Rect insets) {
- this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets, false);
+ this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
+ DOCKED_INVALID, false);
}
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
- boolean isHorizontalDivision, Rect insets, boolean isMinimizedMode) {
+ boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode) {
mMinFlingVelocityPxPerSecond =
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
mMinDismissVelocityPxPerSecond =
@@ -121,7 +126,7 @@ public class DividerSnapAlgorithm {
com.android.internal.R.dimen.default_minimal_size_resizable_task);
mTaskHeightInMinimizedMode = res.getDimensionPixelSize(
com.android.internal.R.dimen.task_height_of_minimized_mode);
- calculateTargets(isHorizontalDivision);
+ calculateTargets(isHorizontalDivision, dockSide);
mFirstSplitTarget = mTargets.get(1);
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
mDismissStartTarget = mTargets.get(0);
@@ -254,7 +259,7 @@ public class DividerSnapAlgorithm {
return mTargets.get(minIndex);
}
- private void calculateTargets(boolean isHorizontalDivision) {
+ private void calculateTargets(boolean isHorizontalDivision, int dockedSide) {
mTargets.clear();
int dividerMax = isHorizontalDivision
? mDisplayHeight
@@ -273,7 +278,7 @@ public class DividerSnapAlgorithm {
addMiddleTarget(isHorizontalDivision);
break;
case SNAP_MODE_MINIMIZED:
- addMinimizedTarget(isHorizontalDivision);
+ addMinimizedTarget(isHorizontalDivision, dockedSide);
break;
}
mTargets.add(new SnapTarget(dividerMax - navBarSize, dividerMax,
@@ -331,12 +336,16 @@ public class DividerSnapAlgorithm {
mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
}
- private void addMinimizedTarget(boolean isHorizontalDivision) {
+ private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) {
// In portrait offset the position by the statusbar height, in landscape add the statusbar
// height as well to match portrait offset
int position = mTaskHeightInMinimizedMode + mInsets.top;
if (!isHorizontalDivision) {
- position += mInsets.left;
+ if (dockedSide == DOCKED_LEFT) {
+ position += mInsets.left;
+ } else if (dockedSide == DOCKED_RIGHT) {
+ position = mDisplayWidth - position - mInsets.right;
+ }
}
mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 8d9630fe5654..e5ad1f47d37d 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -804,7 +804,7 @@ public class StateMachine {
/** State that processed the message */
State msgProcessedState = null;
- if (mIsConstructionCompleted) {
+ if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
/** Normal path */
msgProcessedState = processMsg(msg);
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 073aac542e31..26023b499919 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -16,7 +16,10 @@
package com.android.internal.widget;
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Pair;
@@ -37,6 +40,7 @@ import java.util.Comparator;
@RemoteViews.RemoteView
public class NotificationActionListLayout extends LinearLayout {
+ private final int mGravity;
private int mTotalWidth = 0;
private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
@@ -45,7 +49,20 @@ public class NotificationActionListLayout extends LinearLayout {
private Drawable mDefaultBackground;
public NotificationActionListLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
+ }
+
+ public NotificationActionListLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public NotificationActionListLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ int[] attrIds = { android.R.attr.gravity };
+ TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
+ mGravity = ta.getInt(0, 0);
+ ta.recycle();
}
@Override
@@ -95,6 +112,7 @@ public class NotificationActionListLayout extends LinearLayout {
final boolean constrained =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED;
+ final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
final int otherSize = mMeasureOrderOther.size();
@@ -137,7 +155,7 @@ public class NotificationActionListLayout extends LinearLayout {
// Make sure to measure the last child full-width if we didn't use up the entire width,
// or we didn't measure yet because there's just one child.
- if (lastNotGoneChild != null && (constrained && usedWidth < innerWidth
+ if (lastNotGoneChild != null && !centerAligned && (constrained && usedWidth < innerWidth
|| notGoneChildren == 1)) {
MarginLayoutParams lp = (MarginLayoutParams) lastNotGoneChild.getLayoutParams();
if (notGoneChildren > 1) {
@@ -201,9 +219,10 @@ public class NotificationActionListLayout extends LinearLayout {
}
final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
+ final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
int childTop;
- int childLeft;
+ int childLeft = centerAligned ? left + (right - left) / 2 - mTotalWidth / 2 : 0;
// Where bottom of child should go
final int height = bottom - top;
@@ -216,13 +235,12 @@ public class NotificationActionListLayout extends LinearLayout {
final int layoutDirection = getLayoutDirection();
switch (Gravity.getAbsoluteGravity(Gravity.START, layoutDirection)) {
case Gravity.RIGHT:
- // mTotalWidth contains the padding already
- childLeft = mPaddingLeft + right - left - mTotalWidth;
+ childLeft += mPaddingLeft + right - left - mTotalWidth;
break;
case Gravity.LEFT:
default:
- childLeft = mPaddingLeft;
+ childLeft += mPaddingLeft;
break;
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index f88db259425d..fb8b9f75e031 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -185,7 +185,6 @@ cc_library_shared {
"android/opengl/poly_clip.cpp", // TODO: .arm
"android/opengl/util.cpp",
"android_server_NetworkManagementSocketTagger.cpp",
- "android_server_Watchdog.cpp",
"android_ddm_DdmHandleNativeHeap.cpp",
"android_backup_BackupDataInput.cpp",
"android_backup_BackupDataOutput.cpp",
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index c4f22eeb8b95..f6783e15e294 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -296,7 +296,8 @@ static jobject JHwBinder_native_getService(
JNIEnv *env,
jclass /* clazzObj */,
jstring ifaceNameObj,
- jstring serviceNameObj) {
+ jstring serviceNameObj,
+ jboolean retry) {
using ::android::hidl::base::V1_0::IBase;
using ::android::hardware::details::getRawServiceInternal;
@@ -319,8 +320,7 @@ static jobject JHwBinder_native_getService(
serviceName = str.c_str();
}
- // TODO(b/67981006): true /* retry */
- sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, false /* retry */, false /* getStub */);
+ sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, retry /* retry */, false /* getStub */);
sp<hardware::IBinder> service = hardware::toBinder<hidl::base::V1_0::IBase>(ret);
if (service == NULL) {
@@ -344,6 +344,17 @@ void JHwBinder_native_joinRpcThreadpool() {
IPCThreadState::self()->joinThreadPool();
}
+void JHwBinder_native_startRpcThreadPool(JNIEnv *, jclass,
+ jlong maxThreads, jboolean callerWillJoin) {
+ CHECK(maxThreads > 0);
+ ProcessState::self()->setThreadPoolConfiguration(maxThreads,
+ callerWillJoin /* callerJoinsPool */);
+ ssize_t threadsNeeded = maxThreads - (callerWillJoin ? 0 : 1);
+ for (ssize_t i = 0; i < threadsNeeded; ++i) {
+ ProcessState::self()->spawnPooledThread(false /* isMain */);
+ }
+}
+
static void JHwBinder_report_sysprop_change(JNIEnv * /*env*/, jclass /*clazz*/)
{
report_sysprop_change();
@@ -360,7 +371,7 @@ static JNINativeMethod gMethods[] = {
{ "registerService", "(Ljava/lang/String;)V",
(void *)JHwBinder_native_registerService },
- { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
+ { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;",
(void *)JHwBinder_native_getService },
{ "configureRpcThreadpool", "(JZ)V",
@@ -369,6 +380,9 @@ static JNINativeMethod gMethods[] = {
{ "joinRpcThreadpool", "()V",
(void *)JHwBinder_native_joinRpcThreadpool },
+ { "startRpcThreadPool", "(JZ)V",
+ (void *)JHwBinder_native_startRpcThreadPool },
+
{ "native_report_sysprop_change", "()V",
(void *)JHwBinder_report_sysprop_change },
};
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index 40d49b7662d9..bb916d2431c5 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -26,6 +26,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include "core_jni_helpers.h"
@@ -60,12 +61,12 @@ sp<JHwBlob> JHwBlob::SetNativeContext(
JNIEnv *env, jobject thiz, const sp<JHwBlob> &context) {
sp<JHwBlob> old = (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
- if (context != NULL) {
- context->incStrong(NULL /* id */);
+ if (context != nullptr) {
+ context->incStrong(nullptr /* id */);
}
- if (old != NULL) {
- old->decStrong(NULL /* id */);
+ if (old != nullptr) {
+ old->decStrong(nullptr /* id */);
}
env->SetLongField(thiz, gFields.contextID, (long)context.get());
@@ -150,6 +151,10 @@ const void *JHwBlob::data() const {
return mBuffer;
}
+void *JHwBlob::data() {
+ return mBuffer;
+}
+
size_t JHwBlob::size() const {
return mSize;
}
@@ -242,8 +247,8 @@ using namespace android;
static void releaseNativeContext(void *nativeContext) {
sp<JHwBlob> parcel = (JHwBlob *)nativeContext;
- if (parcel != NULL) {
- parcel->decStrong(NULL /* id */);
+ if (parcel != nullptr) {
+ parcel->decStrong(nullptr /* id */);
}
}
@@ -313,6 +318,82 @@ static jstring JHwBlob_native_getString(
return env->NewStringUTF(s->c_str());
}
+#define DEFINE_BLOB_ARRAY_COPIER(Suffix,Type,NewType) \
+static void JHwBlob_native_copyTo ## Suffix ## Array( \
+ JNIEnv *env, \
+ jobject thiz, \
+ jlong offset, \
+ Type ## Array array, \
+ jint size) { \
+ if (array == nullptr) { \
+ jniThrowException(env, "java/lang/NullPointerException", nullptr); \
+ return; \
+ } \
+ \
+ if (env->GetArrayLength(array) < size) { \
+ signalExceptionForError(env, BAD_VALUE); \
+ return; \
+ } \
+ \
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
+ \
+ if ((offset + size * sizeof(Type)) > blob->size()) { \
+ signalExceptionForError(env, -ERANGE); \
+ return; \
+ } \
+ \
+ env->Set ## NewType ## ArrayRegion( \
+ array, \
+ 0 /* start */, \
+ size, \
+ reinterpret_cast<const Type *>( \
+ static_cast<const uint8_t *>(blob->data()) + offset)); \
+}
+
+DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte)
+DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short)
+DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int)
+DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long)
+DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float)
+DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double)
+
+static void JHwBlob_native_copyToBoolArray(
+ JNIEnv *env,
+ jobject thiz,
+ jlong offset,
+ jbooleanArray array,
+ jint size) {
+ if (array == nullptr) {
+ jniThrowException(env, "java/lang/NullPointerException", nullptr);
+ return;
+ }
+
+ if (env->GetArrayLength(array) < size) {
+ signalExceptionForError(env, BAD_VALUE);
+ return;
+ }
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+ if ((offset + size * sizeof(bool)) > blob->size()) {
+ signalExceptionForError(env, -ERANGE);
+ return;
+ }
+
+ const bool *src =
+ reinterpret_cast<const bool *>(
+ static_cast<const uint8_t *>(blob->data()) + offset);
+
+ jboolean *dst = env->GetBooleanArrayElements(array, nullptr /* isCopy */);
+
+ for (jint i = 0; i < size; ++i) {
+ dst[i] = src[i];
+ }
+
+ env->ReleaseBooleanArrayElements(array, dst, 0 /* mode */);
+ dst = nullptr;
+}
+
#define DEFINE_BLOB_PUTTER(Suffix,Type) \
static void JHwBlob_native_put ## Suffix( \
JNIEnv *env, jobject thiz, jlong offset, Type x) { \
@@ -375,6 +456,59 @@ static void JHwBlob_native_putString(
blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob);
}
+#define DEFINE_BLOB_ARRAY_PUTTER(Suffix,Type,NewType) \
+static void JHwBlob_native_put ## Suffix ## Array( \
+ JNIEnv *env, jobject thiz, jlong offset, Type ## Array array) { \
+ Scoped ## NewType ## ArrayRO autoArray(env, array); \
+ \
+ if (array == nullptr) { \
+ /* NullpointerException already pending */ \
+ return; \
+ } \
+ \
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
+ \
+ status_t err = blob->write( \
+ offset, autoArray.get(), autoArray.size() * sizeof(Type)); \
+ \
+ if (err != OK) { \
+ signalExceptionForError(env, err); \
+ } \
+}
+
+DEFINE_BLOB_ARRAY_PUTTER(Int8,jbyte,Byte)
+DEFINE_BLOB_ARRAY_PUTTER(Int16,jshort,Short)
+DEFINE_BLOB_ARRAY_PUTTER(Int32,jint,Int)
+DEFINE_BLOB_ARRAY_PUTTER(Int64,jlong,Long)
+DEFINE_BLOB_ARRAY_PUTTER(Float,jfloat,Float)
+DEFINE_BLOB_ARRAY_PUTTER(Double,jdouble,Double)
+
+static void JHwBlob_native_putBoolArray(
+ JNIEnv *env, jobject thiz, jlong offset, jbooleanArray array) {
+ ScopedBooleanArrayRO autoArray(env, array);
+
+ if (array == nullptr) {
+ /* NullpointerException already pending */
+ return;
+ }
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+ if ((offset + autoArray.size() * sizeof(bool)) > blob->size()) {
+ signalExceptionForError(env, -ERANGE);
+ return;
+ }
+
+ const jboolean *src = autoArray.get();
+
+ bool *dst = reinterpret_cast<bool *>(
+ static_cast<uint8_t *>(blob->data()) + offset);
+
+ for (size_t i = 0; i < autoArray.size(); ++i) {
+ dst[i] = src[i];
+ }
+}
+
static void JHwBlob_native_putBlob(
JNIEnv *env, jobject thiz, jlong offset, jobject blobObj) {
if (blobObj == nullptr) {
@@ -413,6 +547,14 @@ static JNINativeMethod gMethods[] = {
{ "getDouble", "(J)D", (void *)JHwBlob_native_getDouble },
{ "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString },
+ { "copyToBoolArray", "(J[ZI)V", (void *)JHwBlob_native_copyToBoolArray },
+ { "copyToInt8Array", "(J[BI)V", (void *)JHwBlob_native_copyToInt8Array },
+ { "copyToInt16Array", "(J[SI)V", (void *)JHwBlob_native_copyToInt16Array },
+ { "copyToInt32Array", "(J[II)V", (void *)JHwBlob_native_copyToInt32Array },
+ { "copyToInt64Array", "(J[JI)V", (void *)JHwBlob_native_copyToInt64Array },
+ { "copyToFloatArray", "(J[FI)V", (void *)JHwBlob_native_copyToFloatArray },
+ { "copyToDoubleArray", "(J[DI)V", (void *)JHwBlob_native_copyToDoubleArray },
+
{ "putBool", "(JZ)V", (void *)JHwBlob_native_putBool },
{ "putInt8", "(JB)V", (void *)JHwBlob_native_putInt8 },
{ "putInt16", "(JS)V", (void *)JHwBlob_native_putInt16 },
@@ -422,6 +564,14 @@ static JNINativeMethod gMethods[] = {
{ "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble },
{ "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString },
+ { "putBoolArray", "(J[Z)V", (void *)JHwBlob_native_putBoolArray },
+ { "putInt8Array", "(J[B)V", (void *)JHwBlob_native_putInt8Array },
+ { "putInt16Array", "(J[S)V", (void *)JHwBlob_native_putInt16Array },
+ { "putInt32Array", "(J[I)V", (void *)JHwBlob_native_putInt32Array },
+ { "putInt64Array", "(J[J)V", (void *)JHwBlob_native_putInt64Array },
+ { "putFloatArray", "(J[F)V", (void *)JHwBlob_native_putFloatArray },
+ { "putDoubleArray", "(J[D)V", (void *)JHwBlob_native_putDoubleArray },
+
{ "putBlob", "(JL" PACKAGE_PATH "/HwBlob;)V",
(void *)JHwBlob_native_putBlob },
diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h
index 39393cb76c23..6b1db639ac19 100644
--- a/core/jni/android_os_HwBlob.h
+++ b/core/jni/android_os_HwBlob.h
@@ -50,6 +50,8 @@ struct JHwBlob : public RefBase {
size_t offset, const android::hardware::hidl_string **s) const;
const void *data() const;
+ void *data();
+
size_t size() const;
status_t putBlob(size_t offset, const sp<JHwBlob> &blob);
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index cf59a56a13dc..ca5e1e45dcdc 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -22,9 +22,13 @@
#include "android_os_HwParcel.h"
-#include <nativehelper/JNIHelp.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hidl/base/1.0/BpHwBase.h>
+#include <android/hidl/base/1.0/BnHwBase.h>
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
+#include <hidl/HidlTransportSupport.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -413,6 +417,44 @@ static jboolean JHwRemoteBinder_unlinkToDeath(JNIEnv* env, jobject thiz,
return res;
}
+static sp<hidl::base::V1_0::IBase> toIBase(JNIEnv* env, jclass hwRemoteBinderClazz, jobject jbinder)
+{
+ if (jbinder == nullptr) {
+ return nullptr;
+ }
+ if (!env->IsInstanceOf(jbinder, hwRemoteBinderClazz)) {
+ return nullptr;
+ }
+ sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, jbinder);
+ sp<hardware::IBinder> cbinder = context->getBinder();
+ return hardware::fromBinder<hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase,
+ hidl::base::V1_0::BnHwBase>(cbinder);
+}
+
+// equals iff other is also a non-null android.os.HwRemoteBinder object
+// and getBinder() returns the same object.
+// In particular, if other is an android.os.HwBinder object (for stubs) then
+// it returns false.
+static jboolean JHwRemoteBinder_equals(JNIEnv* env, jobject thiz, jobject other)
+{
+ if (env->IsSameObject(thiz, other)) {
+ return true;
+ }
+ if (other == NULL) {
+ return false;
+ }
+
+ ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+ return hardware::interfacesEqual(toIBase(env, clazz.get(), thiz), toIBase(env, clazz.get(), other));
+}
+
+static jint JHwRemoteBinder_hashCode(JNIEnv* env, jobject thiz) {
+ jlong longHash = reinterpret_cast<jlong>(
+ JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder().get());
+ return static_cast<jint>(longHash ^ (longHash >> 32)); // See Long.hashCode()
+}
+
static JNINativeMethod gMethods[] = {
{ "native_init", "()J", (void *)JHwRemoteBinder_native_init },
@@ -430,6 +472,11 @@ static JNINativeMethod gMethods[] = {
{"unlinkToDeath",
"(Landroid/os/IHwBinder$DeathRecipient;)Z",
(void*)JHwRemoteBinder_unlinkToDeath},
+
+ {"equals", "(Ljava/lang/Object;)Z",
+ (void*)JHwRemoteBinder_equals},
+
+ {"hashCode", "()I", (void*)JHwRemoteBinder_hashCode},
};
namespace android {
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 5ef2a9e6465c..6243fadc25dc 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -102,8 +102,11 @@ static jint android_os_VintfObject_verify(JNIEnv* env, jclass, jobjectArray pack
cPackageInfo[i] = cString;
env->ReleaseStringUTFChars(element, cString);
}
+ // If we can run this code, the device should already pass AVB.
+ // So, we don't need to check AVB here.
std::string error;
- int32_t status = VintfObject::CheckCompatibility(cPackageInfo, &error);
+ int32_t status = VintfObject::CheckCompatibility(
+ cPackageInfo, &error, ::android::vintf::DISABLE_AVB_CHECK);
if (status)
LOG(WARNING) << "VintfObject.verify() returns " << status << ": " << error;
return status;
diff --git a/core/jni/android_server_Watchdog.cpp b/core/jni/android_server_Watchdog.cpp
deleted file mode 100644
index 01d565b26ddb..000000000000
--- a/core/jni/android_server_Watchdog.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- ** Copyright 2010, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#define LOG_TAG "Watchdog_N"
-#include <utils/Log.h>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <string.h>
-#include <errno.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-static void dumpOneStack(int tid, int outFd) {
- char buf[64];
-
- snprintf(buf, sizeof(buf), "/proc/%d/stack", tid);
- int stackFd = open(buf, O_RDONLY);
- if (stackFd >= 0) {
- // header for readability
- strncat(buf, ":\n", sizeof(buf) - strlen(buf) - 1);
- write(outFd, buf, strlen(buf));
-
- // copy the stack dump text
- int nBytes;
- while ((nBytes = read(stackFd, buf, sizeof(buf))) > 0) {
- write(outFd, buf, nBytes);
- }
-
- // footer and done
- write(outFd, "\n", 1);
- close(stackFd);
- } else {
- ALOGE("Unable to open stack of tid %d : %d (%s)", tid, errno, strerror(errno));
- }
-}
-
-static void dumpKernelStacks(JNIEnv* env, jobject clazz, jstring pathStr) {
- char buf[128];
- DIR* taskdir;
-
- ALOGI("dumpKernelStacks");
- if (!pathStr) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Null path");
- return;
- }
-
- const char *path = env->GetStringUTFChars(pathStr, NULL);
-
- int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT,
- S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
- if (outFd < 0) {
- ALOGE("Unable to open stack dump file: %d (%s)", errno, strerror(errno));
- goto done;
- }
-
- snprintf(buf, sizeof(buf), "\n----- begin pid %d kernel stacks -----\n", getpid());
- write(outFd, buf, strlen(buf));
-
- // look up the list of all threads in this process
- snprintf(buf, sizeof(buf), "/proc/%d/task", getpid());
- taskdir = opendir(buf);
- if (taskdir != NULL) {
- struct dirent * ent;
- while ((ent = readdir(taskdir)) != NULL) {
- int tid = atoi(ent->d_name);
- if (tid > 0 && tid <= 65535) {
- // dump each stack trace
- dumpOneStack(tid, outFd);
- }
- }
- closedir(taskdir);
- }
-
- snprintf(buf, sizeof(buf), "----- end pid %d kernel stacks -----\n", getpid());
- write(outFd, buf, strlen(buf));
-
- close(outFd);
-done:
- env->ReleaseStringUTFChars(pathStr, path);
-}
-
-// ----------------------------------------
-
-namespace android {
-
-static const JNINativeMethod g_methods[] = {
- { "native_dumpKernelStacks", "(Ljava/lang/String;)V", (void*)dumpKernelStacks },
-};
-
-int register_android_server_Watchdog(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "com/android/server/Watchdog", g_methods, NELEM(g_methods));
-}
-
-}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b137da3ef792..c6828c4f60de 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -568,6 +568,35 @@ static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject cla
return (res) ? (jint)cookie : 0;
}
+static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor, jstring debugPathName,
+ jboolean appAsLib)
+{
+ ScopedUtfChars debugPathName8(env, debugPathName);
+
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
+ }
+
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+
+ int dupfd = ::dup(fd);
+ if (dupfd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ int32_t cookie;
+ bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib);
+
+ return (res) ? static_cast<jint>(cookie) : 0;
+}
+
static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
@@ -1673,6 +1702,8 @@ static const JNINativeMethod gAssetManagerMethods[] = {
(void*) android_content_AssetManager_getAssetRemainingLength },
{ "addAssetPathNative", "(Ljava/lang/String;Z)I",
(void*) android_content_AssetManager_addAssetPath },
+ { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I",
+ (void*) android_content_AssetManager_addAssetFd },
{ "addOverlayPathNative", "(Ljava/lang/String;)I",
(void*) android_content_AssetManager_addOverlayPath },
{ "isUpToDate", "()Z",
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 50e811d79845..e998b09d18ee 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -27,6 +27,7 @@ import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
import "frameworks/base/core/proto/android/os/procrank.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/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
@@ -135,4 +136,9 @@ message IncidentProto {
(section).type = SECTION_DUMPSYS,
(section).args = "alarm --proto"
];
+
+ optional com.android.server.wm.proto.WindowManagerServiceProto window = 3017 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "window --proto"
+ ];
}
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index 12649e1f0ab2..d032a452c14e 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
syntax = "proto2";
+
option java_multiple_files = true;
option java_outer_classname = "WakeupSourcesProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
message KernelWakeSources {
@@ -27,6 +29,8 @@ message KernelWakeSources {
// Next Tag: 11
message WakeupSourceProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
+
// Name of the event which triggers application processor
optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index f82ea7672879..22b3d730f1a1 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -18,6 +18,8 @@ syntax = "proto2";
option java_multiple_files = true;
option java_outer_classname = "PageTypeInfoProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
/*
@@ -61,6 +63,7 @@ message MigrateTypeProto {
// Next tag: 9
message BlockProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
optional int32 node = 1;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index ab6a6a32f698..4d62a60c8345 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -18,6 +18,8 @@ syntax = "proto2";
option java_multiple_files = true;
option java_outer_classname = "ProcrankProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
//Memory usage of running processes
@@ -31,6 +33,8 @@ message Procrank {
// Next Tag: 11
message ProcessProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
+
// ID of the process
optional int32 pid = 1;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 064523a16058..4d48a4299281 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -15,6 +15,7 @@
*/
syntax = "proto2";
+
import "frameworks/base/core/proto/android/content/configuration.proto";
import "frameworks/base/core/proto/android/graphics/rect.proto";
import "frameworks/base/core/proto/android/view/displayinfo.proto";
diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto
new file mode 100644
index 000000000000..0c65bb273809
--- /dev/null
+++ b/core/proto/android/server/windowmanagertrace.proto
@@ -0,0 +1,56 @@
+/*
+ * 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/content/configuration.proto";
+import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
+import "frameworks/base/core/proto/android/view/displayinfo.proto";
+import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
+
+package com.android.server.wm.proto;
+
+option java_multiple_files = true;
+
+/* represents a file full of window manager trace entries.
+ Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.WINTRACE), such
+ that they can be easily identified. */
+message WindowManagerTraceFileProto {
+
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x544e4957; /* WINT (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+ }
+
+ optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ repeated WindowManagerTraceProto entry = 2;
+}
+
+/* one window manager trace entry. */
+message WindowManagerTraceProto {
+ /* required: elapsed realtime in nanos since boot of when this entry was logged */
+ optional fixed64 elapsed_realtime_nanos = 1;
+
+ /* where the trace originated */
+ optional string where = 2;
+
+ optional WindowManagerServiceProto window_manager_service = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 507a431e8a28..4a4de248b2a1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -326,6 +326,7 @@
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
<protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
<protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
<protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
@@ -3061,10 +3062,10 @@
android:protectionLevel="signature|privileged|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- <!-- @hide Allows an application to change the app idle state of an app.
+ <!-- @hide @SystemApi Allows an application to change the app idle state of an app.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to
access the network and acquire wakelocks.
diff --git a/core/res/res/drawable/dialog_background_material.xml b/core/res/res/drawable/dialog_background_material.xml
index 2f8d1fa13eed..e017d3c861a7 100644
--- a/core/res/res/drawable/dialog_background_material.xml
+++ b/core/res/res/drawable/dialog_background_material.xml
@@ -17,7 +17,7 @@
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:inset="16dp">
<shape android:shape="rectangle">
- <corners android:radius="2dp" />
+ <corners android:radius="?attr/dialogCornerRadius" />
<solid android:color="?attr/colorBackground" />
</shape>
</inset>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index f0c980c703cc..3a28f4d76f7c 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -37,6 +37,7 @@
android:textAppearance="?attr/notificationHeaderTextAppearance"
android:layout_marginStart="@dimen/notification_header_app_name_margin_start"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:visibility="?attr/notificationHeaderAppNameVisibility"
android:singleLine="true"
/>
<TextView
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index ee5c758cc130..865685ff26f5 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -23,8 +23,8 @@
android:paddingStart="@dimen/notification_extra_margin_ambient"
android:paddingEnd="@dimen/notification_extra_margin_ambient"
>
- <include layout="@layout/notification_template_header"
- android:theme="@style/Theme.Material.Notification.Ambient" />
+ <include layout="@layout/notification_template_ambient_header"
+ android:theme="@style/Theme.Material.Notification.Ambient" />
<LinearLayout
android:id="@+id/notification_action_list_margin_target"
@@ -53,6 +53,7 @@
android:textAppearance="@style/TextAppearance.Material.Notification.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:gravity="top|center_horizontal"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
@@ -65,7 +66,7 @@
android:textAppearance="@style/TextAppearance.Material.Notification"
android:singleLine="false"
android:layout_weight="1"
- android:gravity="top"
+ android:gravity="top|center_horizontal"
android:visibility="gone"
android:textSize="16sp"
android:textColor="#eeffffff"
@@ -75,5 +76,19 @@
/>
</LinearLayout>
</LinearLayout>
- <include layout="@layout/notification_material_action_list" />
+ <FrameLayout android:id="@+id/actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom">
+ <com.android.internal.widget.NotificationActionListLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_action_list_height"
+ android:paddingEnd="4dp"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:visibility="gone"
+ android:background="@color/notification_action_list"
+ />
+ </FrameLayout>
</FrameLayout>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 57dd73f5a192..aa83d1d52b55 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1653,7 +1653,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2. poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3. poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Da otkačite ovaj ekran, dodirnite i držite dugmad Nazad i Pregled."</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Ova aplikacija se ne može otkačiti"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Ekran je zakačen"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Ekran je otkačen"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Traži PIN prije nego se otkači"</string>
@@ -1819,14 +1818,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test poruka za hitne slučajeve"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Odgovori"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM kartica nije dozvoljena za govor"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM kartica nije dodijeljena za govor"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM kartica nije dozvoljena za govor"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefon nije dozvoljen za govor"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Iskočni prozor"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Za ovu prečicu potrebna je najnovija aplikacija"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index cb7500429edd..57312e7444e0 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1635,8 +1635,8 @@
<string name="package_updated_device_owner" msgid="1847154566357862089">"Diupdate oleh admin Anda"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"Dihapus oleh admin Anda"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Untuk membantu meningkatkan masa pakai baterai, penghemat baterai mengurangi kinerja perangkat dan membatasi getaran, layanan lokasi, dan sebagian besar data latar belakang. Email, pesan, dan aplikasi lain yang mengandalkan sinkronisasi mungkin tidak diperbarui kecuali jika dibuka.\n\nPenghemat baterai otomatis nonaktif jika perangkat diisi dayanya."</string>
- <string name="data_saver_description" msgid="6015391409098303235">"Untuk membantu mengurangi penggunaan data, Penghemat Data mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah disentuh."</string>
- <string name="data_saver_enable_title" msgid="4674073932722787417">"Aktifkan Penghemat Data?"</string>
+ <string name="data_saver_description" msgid="6015391409098303235">"Untuk membantu mengurangi penggunaan data, Penghemat Kuota Internet mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah disentuh."</string>
+ <string name="data_saver_enable_title" msgid="4674073932722787417">"Aktifkan Penghemat Kuota Internet?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"Aktifkan"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Selama %1$d menit (hingga <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index aa2cc3d1ed44..1976bbe6bfe0 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1231,7 +1231,7 @@
<string name="ext_media_unmounting_notification_title" msgid="640674168454809372">"아직 <xliff:g id="NAME">%s</xliff:g> 꺼내는 중…"</string>
<string name="ext_media_unmounting_notification_message" msgid="4182843895023357756">"삭제하지 마세요."</string>
<string name="ext_media_init_action" msgid="7952885510091978278">"설정"</string>
- <string name="ext_media_unmount_action" msgid="1121883233103278199">"꺼내기"</string>
+ <string name="ext_media_unmount_action" msgid="1121883233103278199">"마운트 해제"</string>
<string name="ext_media_browse_action" msgid="8322172381028546087">"둘러보기"</string>
<string name="ext_media_missing_title" msgid="620980315821543904">"<xliff:g id="NAME">%s</xliff:g> 없음"</string>
<string name="ext_media_missing_message" msgid="5761133583368750174">"기기 다시 삽입"</string>
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"두 번째 업무용 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"세 번째 업무용<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"이 화면을 고정 해제하려면 \'뒤로\' 및 \'최근 사용\'을 길게 터치하세요."</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"이 앱은 고정 해제할 수 없습니다."</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"화면 고정됨"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"화면 고정 해제됨"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"고정 해제 이전에 PIN 요청"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"긴급 메시지 테스트"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"답장"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM에서 음성이 허용되지 않음"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM이 음성으로 프로비저닝되지 않음"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM에서 음성이 허용되지 않음"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"휴대전화에서 음성이 허용되지 않음"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"팝업 창"</string>
<string name="slice_more_content" msgid="8504342889413274608">"<xliff:g id="NUMBER">%1$d</xliff:g>개 더보기"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"바로가기를 사용하려면 최신 앱이 필요합니다"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index cc6cdc93b76c..d7c10ecb76cb 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1676,7 +1676,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 2"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 3"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Aby odpiąć ten ekran, naciśnij i przytrzymaj Wstecz oraz Przegląd"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Tej aplikacji nie można odpiąć"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Ekran przypięty"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Ekran odpięty"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Podaj PIN, aby odpiąć"</string>
@@ -1852,14 +1851,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test komunikatów alarmowych"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Odpowiedz"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Karta SIM jest niedozwolona w przypadku usług głosowych"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Karta SIM nie jest obsługiwana w przypadku usług głosowych"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Karta SIM jest niedozwolona w przypadku usług głosowych"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefon jest niedozwolony w przypadku usług głosowych"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Wyskakujące okienko"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Ten skrót wymaga zainstalowania najnowszej wersji aplikacji"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index af5cfeca6004..2de83c6106d5 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -610,7 +610,7 @@
<item msgid="3145118944639869809">"Personalizado"</item>
</string-array>
<string-array name="organizationTypes">
- <item msgid="7546335612189115615">"Emprego"</item>
+ <item msgid="7546335612189115615">"Trabalho"</item>
<item msgid="4378074129049520373">"Outro"</item>
<item msgid="3455047468583965104">"Personalizado"</item>
</string-array>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 62b54d2b5c61..429ecf109bfd 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2வது பணி <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3வது பணி <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"இந்தத் திரையை அகற்ற, முந்தையது, மேலோட்டப் பார்வை ஆகிய இரண்டு பொத்தானையும் தொட்டுப் பிடித்திருக்கவும்"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"இந்தப் பயன்பாட்டை அகற்ற முடியாது"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"திரை பின் செய்யப்பட்டது"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"திரையின் பின் அகற்றப்பட்டது"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"அகற்றும் முன் PINஐக் கேள்"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"அவசரக் காலச் செய்திகளுக்கான சோதனை"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"பதிலளிக்கும்"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"குரல் அழைப்பை மேற்கொள்ள இந்த சிம்மிற்கு அனுமதி இல்லை"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"குரல் அழைப்பை மேற்கொள்ளும் வசதி இந்த சிம்மிற்கு இல்லை"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"குரல் அழைப்பை மேற்கொள்ள இந்த சிம்மிற்கு அனுமதி இல்லை"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"குரல் அழைப்பை மேற்கொள்ள இந்த ஃபோனுக்கு அனுமதி இல்லை"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"பாப்அப் சாளரம்"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"இந்த ஷார்ட்கட்டைப் பயன்படுத்த, சமீபத்திய பயன்பாடு வேண்டும்"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f5b391ec6fee..0eefec91e390 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -984,6 +984,8 @@
<attr name="dialogTitleDecorLayout" format="reference" />
<!-- Preferred padding for dialog content. -->
<attr name="dialogPreferredPadding" format="dimension" />
+ <!-- Corner radius of dialogs. -->
+ <attr name="dialogCornerRadius" format="dimension" />
<!-- Theme to use for alert dialogs spawned from this theme. -->
<attr name="alertDialogTheme" format="reference" />
@@ -4530,7 +4532,7 @@
<attr name="maxHeight" />
<!-- Makes the TextView be exactly this many lines tall. -->
<attr name="lines" format="integer" min="0" />
- <!-- Makes the TextView be exactly this many pixels tall.
+ <!-- Makes the TextView be exactly this tall.
You could get the same effect by specifying this number in the
layout parameters. -->
<attr name="height" format="dimension" />
@@ -4547,7 +4549,7 @@
<attr name="maxWidth" />
<!-- Makes the TextView be exactly this many ems wide. -->
<attr name="ems" format="integer" min="0" />
- <!-- Makes the TextView be exactly this many pixels wide.
+ <!-- Makes the TextView be exactly this wide.
You could get the same effect by specifying this number in the
layout parameters. -->
<attr name="width" format="dimension" />
@@ -8673,6 +8675,11 @@
<!-- The index of the font in the tcc font file. If the font file referenced is not in the
tcc format, this attribute needs not be specified. -->
<attr name="ttcIndex" format="integer" />
+ <!-- The variation settings to be applied to the font. The string should be in the following
+ format: "'tag1' value1, 'tag2' value2, ...". If the default variation settings should be
+ used, or the font used does not support variation settings, this attribute needs not be
+ specified. -->
+ <attr name="fontVariationSettings" format="string" />
</declare-styleable>
<!-- Attributes that are read when parsing a <fontfamily> tag. -->
@@ -8708,6 +8715,14 @@
<attr name="notificationHeaderStyle" format="reference" />
<attr name="notificationHeaderTextAppearance" format="reference" />
<attr name="notificationHeaderIconSize" format="dimension" />
+ <attr name="notificationHeaderAppNameVisibility" format="enum">
+ <!-- Visible on screen; the default value. -->
+ <enum name="visible" value="0" />
+ <!-- Not displayed, but taken into account during layout (space is left for it). -->
+ <enum name="invisible" value="1" />
+ <!-- Completely hidden, as if the view had not been added. -->
+ <enum name="gone" value="2" />
+ </attr>
</declare-styleable>
<attr name="lockPatternStyle" format="reference" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c2814d72ebe0..dc68341ed3b2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1698,6 +1698,11 @@
a transaction, so it interacts poorly with SECURE_DELETE. -->
<string name="db_default_journal_mode" translatable="false">TRUNCATE</string>
+ <!-- Enables compatibility WAL mode.
+ In this mode, only database journal mode will be changed, connection pool
+ size will still be limited to a single connection. -->
+ <bool name="db_compatibility_wal_supported">true</bool>
+
<!-- Maximum size of the persistent journal file in bytes.
If the journal file grows to be larger than this amount then SQLite will
truncate it after committing the transaction. -->
@@ -1765,9 +1770,6 @@
<string-array name="config_twoDigitNumberPattern" translatable="false">
</string-array>
- <!-- The VoiceMail default value is displayed to my own number if it is true -->
- <bool name="config_telephony_use_own_number_for_voicemail">false</bool>
-
<!-- If this value is true, Sms encoded as octet is decoded by utf8 decoder.
If false, decoded by Latin decoder. -->
<bool name="config_sms_utf8_support">false</bool>
@@ -3150,4 +3152,7 @@
<!-- Component name of media projection permission dialog -->
<string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
+
+ <!-- Corner radius of system dialogs -->
+ <dimen name="config_dialogCornerRadius">2dp</dimen>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index dc75ba6077e7..947fcf127b9c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -624,4 +624,7 @@
<dimen name="slice_icon_size">24dp</dimen>
<!-- Standard padding used in a slice view -->
<dimen name="slice_padding">16dp</dimen>
+
+ <!-- Default dialog corner radius -->
+ <dimen name="dialog_corner_radius">2dp</dimen>
</resources>
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 04ea0771f8e9..2c4058acc972 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -27,6 +27,7 @@
<item>ar-DJ</item> <!-- Arabic (Djibouti) -->
<item>ar-DZ</item> <!-- Arabic (Algeria) -->
<item>ar-EG</item> <!-- Arabic (Egypt) -->
+ <item>ar-EG-u-nu-latn</item> <!-- Arabic (Egypt,Western Digits) -->
<item>ar-EH</item> <!-- Arabic (Western Sahara) -->
<item>ar-ER</item> <!-- Arabic (Eritrea) -->
<item>ar-IL</item> <!-- Arabic (Israel) -->
@@ -48,6 +49,7 @@
<item>ar-SY</item> <!-- Arabic (Syria) -->
<item>ar-TD</item> <!-- Arabic (Chad) -->
<item>ar-TN</item> <!-- Arabic (Tunisia) -->
+ <item>ar-TN-u-nu-arab</item> <!-- Arabic (Tunisia,Arabic-Indic Digits) -->
<item>ar-XB</item> <!-- Right-to-left pseudolocale -->
<item>ar-YE</item> <!-- Arabic (Yemen) -->
<item>as-IN</item> <!-- Assamese (India) -->
@@ -366,6 +368,7 @@
<item>ms-SG</item> <!-- Malay (Singapore) -->
<item>mt-MT</item> <!-- Maltese (Malta) -->
<item>my-MM</item> <!-- Burmese (Myanmar (Burma)) -->
+ <item>my-MM-u-nu-latn</item> <!-- Burmese (Myanmar (Burma), Western Digits) -->
<item>mzn-IR</item> <!-- Mazanderani (Iran) -->
<item>naq-NA</item> <!-- Nama (Namibia) -->
<item>nb-NO</item> <!-- Norwegian Bokmål (Norway) -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b42fd821d70b..fdd56c410ed2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2846,6 +2846,8 @@
<public-group type="attr" first-id="0x0101056e">
<public name="cantSaveState" />
<public name="ttcIndex" />
+ <public name="fontVariationSettings" />
+ <public name="dialogCornerRadius" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 470ac522aa33..cddf99a2df23 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -1296,6 +1296,11 @@ please see styles_device_defaults.xml.
<item name="layout_marginBottom">@dimen/notification_header_margin_bottom</item>
<item name="paddingStart">@dimen/notification_content_margin_start</item>
<item name="paddingEnd">16dp</item>
+ <item name="gravity">top</item>
+ </style>
+
+ <style name="Notification.Header.Ambient">
+ <item name="gravity">top|center_horizontal</item>
</style>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f301644f8f89..32758e83f9b7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -273,7 +273,6 @@
<java-symbol type="bool" name="config_suspendWhenScreenOffDueToProximity" />
<java-symbol type="bool" name="config_swipeDisambiguation" />
<java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
- <java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" />
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_enableHapticTextHandle" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
@@ -668,6 +667,7 @@
<java-symbol type="string" name="date_time" />
<java-symbol type="string" name="date_time_set" />
<java-symbol type="string" name="date_time_done" />
+ <java-symbol type="bool" name="db_compatibility_wal_supported" />
<java-symbol type="string" name="db_default_journal_mode" />
<java-symbol type="string" name="db_default_sync_mode" />
<java-symbol type="string" name="db_wal_sync_mode" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index bf0c90660392..68d552367423 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -88,6 +88,7 @@ easier.
<!-- Dialog attributes -->
<item name="dialogTheme">@style/Theme.DeviceDefault.Dialog</item>
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
@@ -214,6 +215,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -223,6 +228,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -234,6 +243,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -244,6 +257,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -263,6 +280,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -272,6 +293,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -280,6 +305,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -289,6 +318,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -314,6 +347,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -324,6 +361,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -332,6 +373,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -342,6 +387,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -351,6 +400,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -360,6 +413,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -369,6 +426,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -378,11 +439,19 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
<!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
@@ -394,6 +463,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -401,6 +474,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -441,6 +518,7 @@ easier.
<!-- Dialog attributes -->
<item name="dialogTheme">@style/Theme.DeviceDefault.Light.Dialog</item>
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
@@ -562,6 +640,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_dark</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -570,6 +652,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -579,6 +665,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -590,6 +680,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -600,6 +694,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -609,6 +707,10 @@ easier.
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
<item name="buttonBarStyle">@style/DeviceDefault.Light.ButtonBar.AlertDialog</item>
<item name="borderlessButtonStyle">@style/Widget.DeviceDefault.Light.Button.Borderless.Small</item>
@@ -628,6 +730,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -636,6 +742,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -645,6 +755,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -680,6 +794,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -690,6 +808,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -698,6 +820,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -708,6 +834,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -717,6 +847,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
@@ -724,6 +858,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -731,6 +869,10 @@ easier.
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -750,6 +892,10 @@ easier.
<item name="navigationBarDividerColor">#1f000000</item>
<item name="navigationBarColor">@android:color/white</item>
<item name="windowLightNavigationBar">true</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- @hide DeviceDefault theme for a window that should use Settings theme colors
@@ -761,6 +907,7 @@ easier.
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorControlNormal">?attr/textColorPrimary</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -769,6 +916,7 @@ easier.
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -778,6 +926,10 @@ easier.
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
@@ -786,6 +938,10 @@ easier.
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
@@ -794,6 +950,10 @@ easier.
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -802,9 +962,13 @@ easier.
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
</style>
- <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar"/>
+ <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
<!-- Theme used for the intent picker activity. -->
<style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light">
@@ -820,6 +984,10 @@ easier.
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+
<!-- Color palette -->
<item name="colorPrimary">@color/primary_device_default_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
@@ -831,7 +999,7 @@ easier.
<style name="ThemeOverlay.DeviceDefault" />
- <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for
+ <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for
primary text -->
<style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar">
<item name="textColorPrimary">@color/btn_colored_borderless_text_material</item>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 9bea3eeeb9ac..c31712136eed 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -186,6 +186,7 @@ please see themes_device_defaults.xml.
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_material</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_material</item>
<item name="dialogPreferredPadding">@dimen/dialog_padding_material</item>
+ <item name="dialogCornerRadius">@dimen/dialog_corner_radius</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@style/ThemeOverlay.Material.Dialog.Alert</item>
@@ -554,6 +555,7 @@ please see themes_device_defaults.xml.
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_material</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_material</item>
<item name="dialogPreferredPadding">@dimen/dialog_padding_material</item>
+ <item name="dialogCornerRadius">@dimen/dialog_corner_radius</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@style/ThemeOverlay.Material.Dialog.Alert</item>
@@ -1325,12 +1327,15 @@ please see themes_device_defaults.xml.
<style name="Theme.Material.Notification" parent="">
<item name="notificationHeaderStyle">@style/Notification.Header</item>
<item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info</item>
+ <item name="notificationHeaderAppNameVisibility">visible</item>
<item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item>
</style>
<!-- Theme for inflating ambient notification -->
<style name="Theme.Material.Notification.Ambient">
+ <item name="notificationHeaderStyle">@style/Notification.Header.Ambient</item>
<item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item>
+ <item name="notificationHeaderAppNameVisibility">gone</item>
<item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item>
</style>
diff --git a/core/tests/coretests/res/font/samplexmlfont.xml b/core/tests/coretests/res/font/samplexmlfont.xml
index 59d615f9633d..f1d14ff383fd 100644
--- a/core/tests/coretests/res/font/samplexmlfont.xml
+++ b/core/tests/coretests/res/font/samplexmlfont.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
- <font android:fontStyle="normal" android:fontWeight="400" android:font="@font/samplefont"
- android:ttcIndex="0"/>
- <font android:fontStyle="italic" android:fontWeight="400" android:font="@font/samplefont2"
- android:ttcIndex="1"/>
- <font android:fontStyle="normal" android:fontWeight="800" android:font="@font/samplefont3"
- android:ttcIndex="2" />
+ <font android:fontStyle="normal" android:fontWeight="400" android:fontVariationSettings="'wdth' 0.8"
+ android:font="@font/samplefont" android:ttcIndex="0"/>
+ <font android:fontStyle="italic" android:fontWeight="400" android:fontVariationSettings="'contrast' 0.5"
+ android:font="@font/samplefont2" android:ttcIndex="1" />
+ <font android:fontStyle="normal" android:fontWeight="800" android:fontVariationSettings="'wdth' 500.0, 'wght' 300.0"
+ android:font="@font/samplefont3" android:ttcIndex="2" />
<font android:fontStyle="italic" android:fontWeight="800" android:font="@font/samplefont4" />
-</font-family> \ No newline at end of file
+</font-family>
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 4d1a9f4ff10b..a12a0b8d125b 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -69,22 +69,26 @@ public class FontResourcesParserTest {
FontFileResourceEntry font1 = fileEntries[0];
assertEquals(400, font1.getWeight());
assertEquals(0, font1.getItalic());
+ assertEquals("'wdth' 0.8", font1.getVariationSettings());
assertEquals(0, font1.getTtcIndex());
assertEquals("res/font/samplefont.ttf", font1.getFileName());
FontFileResourceEntry font2 = fileEntries[1];
assertEquals(400, font2.getWeight());
assertEquals(1, font2.getItalic());
assertEquals(1, font2.getTtcIndex());
+ assertEquals("'contrast' 0.5", font2.getVariationSettings());
assertEquals("res/font/samplefont2.ttf", font2.getFileName());
FontFileResourceEntry font3 = fileEntries[2];
assertEquals(800, font3.getWeight());
assertEquals(0, font3.getItalic());
assertEquals(2, font3.getTtcIndex());
+ assertEquals("'wdth' 500.0, 'wght' 300.0", font3.getVariationSettings());
assertEquals("res/font/samplefont3.ttf", font3.getFileName());
FontFileResourceEntry font4 = fileEntries[3];
assertEquals(800, font4.getWeight());
assertEquals(1, font4.getItalic());
assertEquals(0, font4.getTtcIndex());
+ assertEquals(null, font4.getVariationSettings());
assertEquals("res/font/samplefont4.ttf", font4.getFileName());
}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 3e0348123618..bbdbdb13b3f7 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -26,12 +26,10 @@ import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
-import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
@@ -61,6 +59,7 @@ import android.support.test.espresso.action.EspressoKey;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.Suppress;
import android.text.InputType;
import android.text.Selection;
import android.text.Spannable;
@@ -216,7 +215,6 @@ public class TextViewActivityTest {
onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
onView(withId(R.id.textview)).check(hasSelection(""));
- assertNoSelectionHandles();
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
// Test undo returns to the original state.
@@ -269,18 +267,12 @@ public class TextViewActivityTest {
@Test
public void testToolbarAppearsAfterSelection() {
final String text = "Toolbar appears after selection.";
- assertFloatingToolbarIsNotDisplayed();
onView(withId(R.id.textview)).perform(replaceText(text));
onView(withId(R.id.textview)).perform(
longPressOnTextAtIndex(text.indexOf("appears")));
sleepForFloatingToolbarPopup();
assertFloatingToolbarIsDisplayed();
-
- final String text2 = "Toolbar disappears after typing text.";
- onView(withId(R.id.textview)).perform(replaceText(text2));
- sleepForFloatingToolbarPopup();
- assertFloatingToolbarIsNotDisplayed();
}
@Test
@@ -310,7 +302,6 @@ public class TextViewActivityTest {
@Test
public void testToolbarAndInsertionHandle() {
final String text = "text";
- assertFloatingToolbarIsNotDisplayed();
onView(withId(R.id.textview)).perform(replaceText(text));
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
@@ -404,8 +395,6 @@ public class TextViewActivityTest {
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(replaceText(text));
- assertNoSelectionHandles();
-
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
onHandleView(com.android.internal.R.id.selection_start_handle)
@@ -428,8 +417,6 @@ public class TextViewActivityTest {
final String text = "abc \u0621\u0622\u0623 def";
onView(withId(R.id.textview)).perform(replaceText(text));
- assertNoSelectionHandles();
-
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0622')));
onHandleView(com.android.internal.R.id.selection_start_handle)
@@ -491,6 +478,7 @@ public class TextViewActivityTest {
onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr"));
}
+ @Suppress // Consistently failing.
@Test
public void testSelectionHandles_multiLine_rtl() {
// Arabic text.
@@ -649,13 +637,11 @@ public class TextViewActivityTest {
onView(withId(R.id.textview)).perform(replaceText(text));
final TextView textView = mActivity.findViewById(R.id.textview);
- assertFloatingToolbarIsNotDisplayed();
mActivityRule.runOnUiThread(
() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
mInstrumentation.waitForIdleSync();
- sleepForFloatingToolbarPopup();
// Don't automatically start action mode.
- assertFloatingToolbarIsNotDisplayed();
+ // TODO: Implement assertActionModeNotStarted()
// Make sure that "Select All" is included in the selection action mode when the entire text
// is not selected.
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
@@ -685,8 +671,6 @@ public class TextViewActivityTest {
() -> Selection.setSelection((Spannable) textView.getText(), 0));
mInstrumentation.waitForIdleSync();
- sleepForFloatingToolbarPopup();
- assertFloatingToolbarIsNotDisplayed();
// Make sure that user click can trigger the insertion action mode.
onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
diff --git a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
index 6a2233ba126e..1693e548f7bd 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
@@ -36,6 +36,10 @@ public final class DragHandleUtils {
private DragHandleUtils() {}
+ /**
+ * @deprecated Negative assertions are taking too long to timeout in Espresso.
+ */
+ @Deprecated
public static void assertNoSelectionHandles() {
try {
onView(isAssignableFrom(Editor.SelectionHandleView.class))
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index f7069b31710f..b6986d5c5a68 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -87,7 +87,9 @@ public class FloatingToolbarEspressoUtils {
* Asserts that the floating toolbar is not displayed on screen.
*
* @throws AssertionError if the assertion fails
+ * @deprecated Negative assertions are taking too long to timeout in Espresso.
*/
+ @Deprecated
public static void assertFloatingToolbarIsNotDisplayed() {
try {
onFloatingToolBar().check(matches(isDisplayed()));
@@ -173,12 +175,31 @@ public class FloatingToolbarEspressoUtils {
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
- try{
- assertFloatingToolbarContainsItem(itemLabel);
- } catch (AssertionError e) {
- return;
- }
- throw new AssertionError("Floating toolbar contains " + itemLabel);
+ onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
+ @Override
+ public boolean matchesSafely(View view) {
+ return doesNotContainItem(view);
+ }
+
+ @Override
+ public void describeTo(Description description) {}
+
+ private boolean doesNotContainItem(View view) {
+ if (view.getTag() instanceof MenuItem) {
+ if (itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString())) {
+ return false;
+ }
+ } else if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ if (doesNotContainItem(viewGroup.getChildAt(i))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }));
}
/**
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index eb2a5165dc4f..76aa93f7e8be 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -24,6 +24,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.Suppress;
import com.android.internal.util.State;
@@ -343,6 +344,100 @@ public class StateMachineTest extends TestCase {
}
/**
+ * Tests {@link StateMachine#quitNow()} immediately after {@link StateMachine#start()}.
+ */
+ class StateMachineQuitNowAfterStartTest extends StateMachine {
+ Collection<LogRec> mLogRecs;
+
+ StateMachineQuitNowAfterStartTest(String name, Looper looper) {
+ super(name, looper);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ @Override
+ public void onQuitting() {
+ tlog("onQuitting");
+ addLogRec(ON_QUITTING);
+ mLogRecs = mThisSm.copyLogRecs();
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ class S1 extends State {
+ @Override
+ public void enter() {
+ tlog("S1.enter");
+ addLogRec(ENTER);
+ }
+ @Override
+ public void exit() {
+ tlog("S1.exit");
+ addLogRec(EXIT);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ // Sleep and assume the other messages will be queued up.
+ case TEST_CMD_1: {
+ tlog("TEST_CMD_1");
+ sleep(500);
+ break;
+ }
+ default: {
+ tlog("default what=" + message.what);
+ break;
+ }
+ }
+ return HANDLED;
+ }
+ }
+
+ private StateMachineQuitNowAfterStartTest mThisSm;
+ private S1 mS1 = new S1();
+ }
+
+ /**
+ * When quitNow() is called immediately after start(), the QUIT_CMD gets processed
+ * before the INIT_CMD. This test ensures that the StateMachine can gracefully handle
+ * this sequencing of messages (QUIT before INIT).
+ */
+ @SmallTest
+ public void testStateMachineQuitNowAfterStart() throws Exception {
+ if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+ TestLooper testLooper = new TestLooper();
+ StateMachineQuitNowAfterStartTest smQuitNowAfterStartTest =
+ new StateMachineQuitNowAfterStartTest(
+ "smQuitNowAfterStartTest", testLooper.getLooper());
+ smQuitNowAfterStartTest.start();
+ smQuitNowAfterStartTest.quitNow();
+ if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart E");
+
+ testLooper.dispatchAll();
+ dumpLogRecs(smQuitNowAfterStartTest.mLogRecs);
+ assertEquals(2, smQuitNowAfterStartTest.mLogRecs.size());
+
+ LogRec lr;
+ Iterator<LogRec> itr = smQuitNowAfterStartTest.mLogRecs.iterator();
+ lr = itr.next();
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smQuitNowAfterStartTest.mS1, lr.getState());
+
+ lr = itr.next();
+ assertEquals(ON_QUITTING, lr.getInfo());
+
+ if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart X");
+ }
+
+ /**
* Test enter/exit can use transitionTo
*/
class StateMachineEnterExitTransitionToTest extends StateMachine {
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
new file mode 100644
index 000000000000..f7a3e1ae8959
--- /dev/null
+++ b/data/etc/OWNERS
@@ -0,0 +1,7 @@
+per-file privapp-permissions-platform.xml = bpoiesz@google.com
+per-file privapp-permissions-platform.xml = fkupolov@google.com
+per-file privapp-permissions-platform.xml = hackbod@android.com
+per-file privapp-permissions-platform.xml = jsharkey@android.com
+per-file privapp-permissions-platform.xml = svetoslavganov@google.com
+per-file privapp-permissions-platform.xml = toddke@google.com
+per-file privapp-permissions-platform.xml = yamasani@google.com
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 9961ed64bb0f..3d65bd226faf 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -250,10 +250,10 @@ public class Typeface {
FontFamily fontFamily = new FontFamily();
for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
- // TODO: Add variation font support. (b/37853920)
if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
0 /* resourceCookie */, false /* isAsset */, fontFile.getTtcIndex(),
- fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
+ fontFile.getWeight(), fontFile.getItalic(),
+ FontVariationAxis.fromFontVariationSettings(fontFile.getVariationSettings()))) {
return null;
}
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 90d6ab867fe1..e74dc6dc9671 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -132,7 +132,7 @@ import dalvik.annotation.optimization.FastNative;
* <td>translateY</td>
* </tr>
* <tr>
- * <td rowspan="8">&lt;path&gt;</td>
+ * <td rowspan="9">&lt;path&gt;</td>
* <td>pathData</td>
* </tr>
* <tr>
@@ -154,6 +154,9 @@ import dalvik.annotation.optimization.FastNative;
* <td>trimPathStart</td>
* </tr>
* <tr>
+ * <td>trimPathEnd</td>
+ * </tr>
+ * <tr>
* <td>trimPathOffset</td>
* </tr>
* <tr>
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index ceac3253e178..7b2e21a40f8b 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -213,12 +213,79 @@ import dalvik.system.VMRuntime;
* &lt;/vector&gt;
* </pre>
* </li>
- * <li>And here is an example of linear gradient color, which is supported in SDK 24+.
+ * <h4>Gradient support</h4>
+ * We support 3 types of gradients: {@link android.graphics.LinearGradient},
+ * {@link android.graphics.RadialGradient}, or {@link android.graphics.SweepGradient}.
+ * <p/>
+ * And we support all of 3 types of tile modes {@link android.graphics.Shader.TileMode}:
+ * CLAMP, REPEAT, MIRROR.
+ * <p/>
+ * All of the attributes are listed in {@link android.R.styleable#GradientColor}.
+ * Note that different attributes are relevant for different types of gradient.
+ * <table border="2" align="center" cellpadding="5">
+ * <thead>
+ * <tr>
+ * <th>LinearGradient</th>
+ * <th>RadialGradient</th>
+ * <th>SweepGradient</th>
+ * </tr>
+ * </thead>
+ * <tr>
+ * <td>startColor </td>
+ * <td>startColor</td>
+ * <td>startColor</td>
+ * </tr>
+ * <tr>
+ * <td>centerColor</td>
+ * <td>centerColor</td>
+ * <td>centerColor</td>
+ * </tr>
+ * <tr>
+ * <td>endColor</td>
+ * <td>endColor</td>
+ * <td>endColor</td>
+ * </tr>
+ * <tr>
+ * <td>type</td>
+ * <td>type</td>
+ * <td>type</td>
+ * </tr>
+ * <tr>
+ * <td>tileMode</td>
+ * <td>tileMode</td>
+ * <td>tileMode</td>
+ * </tr>
+ * <tr>
+ * <td>startX</td>
+ * <td>centerX</td>
+ * <td>centerX</td>
+ * </tr>
+ * <tr>
+ * <td>startY</td>
+ * <td>centerY</td>
+ * <td>centerY</td>
+ * </tr>
+ * <tr>
+ * <td>endX</td>
+ * <td>gradientRadius</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>endY</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ * <p/>
+ * Also note that if any color item {@link android.R.styleable#GradientColorItem} is defined, then
+ * startColor, centerColor and endColor will be ignored.
+ * <p/>
* See more details in {@link android.R.styleable#GradientColor} and
* {@link android.R.styleable#GradientColorItem}.
+ * <p/>
+ * Here is a simple example that defines a linear gradient.
* <pre>
* &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"
- * android:angle="90"
* android:startColor="?android:attr/colorPrimary"
* android:endColor="?android:attr/colorControlActivated"
* android:centerColor="#f00"
@@ -229,7 +296,18 @@ import dalvik.system.VMRuntime;
* android:type="linear"&gt;
* &lt;/gradient&gt;
* </pre>
- * </li>
+ * And here is a simple example that defines a radial gradient using color items.
+ * <pre>
+ * &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:centerX="300"
+ * android:centerY="300"
+ * android:gradientRadius="100"
+ * android:type="radial"&gt;
+ * &lt;item android:offset="0.1" android:color="#0ff"/&gt;
+ * &lt;item android:offset="0.4" android:color="#fff"/&gt;
+ * &lt;item android:offset="0.9" android:color="#ff0"/&gt;
+ * &lt;/gradient&gt;
+ * </pre>
*
*/
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 5603508eaf09..3c8736ea0c4c 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -150,6 +150,14 @@ AssetManager::~AssetManager() {
ALOGI("Destroying AssetManager in %p #%d\n", this, count);
}
+ // Manually close any fd paths for which we have not yet opened their zip (which
+ // will take ownership of the fd and close it when done).
+ for (size_t i=0; i<mAssetPaths.size(); i++) {
+ if (mAssetPaths[i].rawFd >= 0 && mAssetPaths[i].zip == NULL) {
+ close(mAssetPaths[i].rawFd);
+ }
+ }
+
delete mConfig;
delete mResources;
@@ -280,7 +288,35 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
}
return true;
- }
+}
+
+bool AssetManager::addAssetFd(
+ int fd, const String8& debugPathName, int32_t* cookie, bool appAsLib,
+ bool assume_ownership) {
+ AutoMutex _l(mLock);
+
+ asset_path ap;
+
+ ap.path = debugPathName;
+ ap.rawFd = fd;
+ ap.type = kFileTypeRegular;
+ ap.assumeOwnership = assume_ownership;
+
+ ALOGV("In %p Asset fd %d name: %s", this, fd, ap.path.string());
+
+ mAssetPaths.add(ap);
+
+ // new paths are always added at the end
+ if (cookie) {
+ *cookie = static_cast<int32_t>(mAssetPaths.size());
+ }
+
+ if (mResources != NULL) {
+ appendPathToResTable(ap, appAsLib);
+ }
+
+ return true;
+}
bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize)
@@ -505,7 +541,7 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con
Asset* idmap = openIdmapLocked(ap);
size_t nextEntryIdx = mResources->getTableCount();
ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
- if (ap.type != kFileTypeDirectory) {
+ if (ap.type != kFileTypeDirectory && ap.rawFd < 0) {
if (nextEntryIdx == 0) {
// The first item is typically the framework resources,
// which we want to avoid parsing every time.
@@ -738,6 +774,8 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m
{
Asset* pAsset = NULL;
+ ALOGV("openNonAssetInPath: name=%s type=%d fd=%d", fileName, ap.type, ap.rawFd);
+
/* look at the filesystem on disk */
if (ap.type == kFileTypeDirectory) {
String8 path(ap.path);
@@ -752,7 +790,7 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m
}
if (pAsset != NULL) {
- //printf("FOUND NA '%s' on disk\n", fileName);
+ ALOGV("FOUND NA '%s' on disk", fileName);
pAsset->setAssetSource(path);
}
@@ -763,10 +801,10 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m
/* check the appropriate Zip file */
ZipFileRO* pZip = getZipFileLocked(ap);
if (pZip != NULL) {
- //printf("GOT zip, checking NA '%s'\n", (const char*) path);
+ ALOGV("GOT zip, checking NA '%s'", (const char*) path);
ZipEntryRO entry = pZip->findEntryByName(path.string());
if (entry != NULL) {
- //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon);
+ ALOGV("FOUND NA in Zip file for %s", (const char*) path);
pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
pZip->releaseEntry(entry);
}
@@ -817,7 +855,17 @@ ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap)
{
ALOGV("getZipFileLocked() in %p\n", this);
- return mZipSet.getZip(ap.path);
+ if (ap.zip != NULL) {
+ return ap.zip->getZip();
+ }
+
+ if (ap.rawFd < 0) {
+ ap.zip = mZipSet.getSharedZip(ap.path);
+ } else {
+ ap.zip = SharedZip::create(ap.rawFd, ap.path);
+
+ }
+ return ap.zip != NULL ? ap.zip->getZip() : NULL;
}
/*
@@ -1374,6 +1422,21 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
}
}
+AssetManager::SharedZip::SharedZip(int fd, const String8& path)
+ : mPath(path), mZipFile(NULL), mModWhen(0),
+ mResourceTableAsset(NULL), mResourceTable(NULL)
+{
+ if (kIsDebug) {
+ ALOGI("Creating SharedZip %p fd=%d %s\n", this, fd, (const char*)mPath);
+ }
+ ALOGV("+++ opening zip fd=%d '%s'\n", fd, mPath.string());
+ mZipFile = ZipFileRO::openFd(fd, mPath.string());
+ if (mZipFile == NULL) {
+ ::close(fd);
+ ALOGD("failed to open Zip archive fd=%d '%s'\n", fd, mPath.string());
+ }
+}
+
sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
bool createIfNotPresent)
{
@@ -1389,7 +1452,11 @@ sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
zip = new SharedZip(path, modWhen);
gOpen.add(path, zip);
return zip;
+}
+sp<AssetManager::SharedZip> AssetManager::SharedZip::create(int fd, const String8& path)
+{
+ return new SharedZip(fd, path);
}
ZipFileRO* AssetManager::SharedZip::getZip()
@@ -1500,19 +1567,23 @@ void AssetManager::ZipSet::closeZip(int idx)
mZipFile.editItemAt(idx) = NULL;
}
-
/*
* Retrieve the appropriate Zip file from the set.
*/
ZipFileRO* AssetManager::ZipSet::getZip(const String8& path)
{
+ return getSharedZip(path)->getZip();
+}
+
+const sp<AssetManager::SharedZip> AssetManager::ZipSet::getSharedZip(const String8& path)
+{
int idx = getIndex(path);
sp<SharedZip> zip = mZipFile[idx];
if (zip == NULL) {
zip = SharedZip::get(path);
mZipFile.editItemAt(idx) = zip;
}
- return zip->getZip();
+ return zip;
}
Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path)
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 87999c353a90..17de8fa1e7a5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2847,14 +2847,111 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can
}
memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
}
-}
-/* static */ inline bool assignLocaleComponent(ResTable_config* config,
- const char* start, size_t size) {
+ /* TODO: Add BCP47 extension. It requires RESTABLE_MAX_LOCALE_LEN
+ * increase from 28 to 42 bytes (-u-nu-xxxxxxxx) */
+}
+
+struct LocaleParserState {
+ enum State : uint8_t {
+ BASE, UNICODE_EXTENSION, IGNORE_THE_REST
+ } parserState;
+ enum UnicodeState : uint8_t {
+ /* Initial state after the Unicode singleton is detected. Either a keyword
+ * or an attribute is expected. */
+ NO_KEY,
+ /* Unicode extension key (but not attribute) is expected. Next states:
+ * NO_KEY, IGNORE_KEY or NUMBERING_SYSTEM. */
+ EXPECT_KEY,
+ /* A key is detected, however it is not supported for now. Ignore its
+ * value. Next states: IGNORE_KEY or NUMBERING_SYSTEM. */
+ IGNORE_KEY,
+ /* Numbering system key was detected. Store its value in the configuration
+ * localeNumberingSystem field. Next state: EXPECT_KEY */
+ NUMBERING_SYSTEM
+ } unicodeState;
+
+ LocaleParserState(): parserState(BASE), unicodeState(NO_KEY) {}
+};
+
+/* static */ inline LocaleParserState assignLocaleComponent(ResTable_config* config,
+ const char* start, size_t size, LocaleParserState state) {
+
+ /* It is assumed that this function is not invoked with state.parserState
+ * set to IGNORE_THE_REST. The condition is checked by setBcp47Locale
+ * function. */
+
+ if (state.parserState == LocaleParserState::UNICODE_EXTENSION) {
+ switch (size) {
+ case 1:
+ /* Other BCP 47 extensions are not supported at the moment */
+ state.parserState = LocaleParserState::IGNORE_THE_REST;
+ break;
+ case 2:
+ if (state.unicodeState == LocaleParserState::NO_KEY ||
+ state.unicodeState == LocaleParserState::EXPECT_KEY) {
+ /* Analyze Unicode extension key. Currently only 'nu'
+ * (numbering system) is supported.*/
+ if ((start[0] == 'n' || start[0] == 'N') &&
+ (start[1] == 'u' || start[1] == 'U')) {
+ state.unicodeState = LocaleParserState::NUMBERING_SYSTEM;
+ } else {
+ state.unicodeState = LocaleParserState::IGNORE_KEY;
+ }
+ } else {
+ /* Keys are not allowed in other state allowed, ignore the rest. */
+ state.parserState = LocaleParserState::IGNORE_THE_REST;
+ }
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ switch (state.unicodeState) {
+ case LocaleParserState::NUMBERING_SYSTEM:
+ /* Accept only the first occurrence of the numbering system. */
+ if (config->localeNumberingSystem[0] == '\0') {
+ for (size_t i = 0; i < size; ++i) {
+ config->localeNumberingSystem[i] = tolower(start[i]);
+ }
+ state.unicodeState = LocaleParserState::EXPECT_KEY;
+ } else {
+ state.parserState = LocaleParserState::IGNORE_THE_REST;
+ }
+ break;
+ case LocaleParserState::IGNORE_KEY:
+ /* Unsupported Unicode keyword. Ignore. */
+ state.unicodeState = LocaleParserState::EXPECT_KEY;
+ break;
+ case LocaleParserState::EXPECT_KEY:
+ /* A keyword followed by an attribute is not allowed. */
+ state.parserState = LocaleParserState::IGNORE_THE_REST;
+ break;
+ case LocaleParserState::NO_KEY:
+ /* Extension attribute. Do nothing. */
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ /* Unexpected field length - ignore the rest and treat as an error */
+ state.parserState = LocaleParserState::IGNORE_THE_REST;
+ }
+ return state;
+ }
switch (size) {
case 0:
- return false;
+ state.parserState = LocaleParserState::IGNORE_THE_REST;
+ break;
+ case 1:
+ state.parserState = (start[0] == 'u' || start[0] == 'U')
+ ? LocaleParserState::UNICODE_EXTENSION
+ : LocaleParserState::IGNORE_THE_REST;
+ break;
case 2:
case 3:
config->language[0] ? config->packRegion(start) : config->packLanguage(start);
@@ -2878,30 +2975,35 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can
}
break;
default:
- return false;
+ state.parserState = LocaleParserState::IGNORE_THE_REST;
}
- return true;
+ return state;
}
void ResTable_config::setBcp47Locale(const char* in) {
locale = 0;
memset(localeScript, 0, sizeof(localeScript));
memset(localeVariant, 0, sizeof(localeVariant));
+ memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem));
- const char* separator = in;
const char* start = in;
- while ((separator = strchr(start, '-')) != NULL) {
+ LocaleParserState state;
+ while (const char* separator = strchr(start, '-')) {
const size_t size = separator - start;
- if (!assignLocaleComponent(this, start, size)) {
- fprintf(stderr, "Invalid BCP-47 locale string: %s", in);
+ state = assignLocaleComponent(this, start, size, state);
+ if (state.parserState == LocaleParserState::IGNORE_THE_REST) {
+ fprintf(stderr, "Invalid BCP-47 locale string: %s\n", in);
+ break;
}
-
start = (separator + 1);
}
- const size_t size = in + strlen(in) - start;
- assignLocaleComponent(this, start, size);
+ if (state.parserState != LocaleParserState::IGNORE_THE_REST) {
+ const size_t size = strlen(start);
+ assignLocaleComponent(this, start, size, state);
+ }
+
localeScriptWasComputed = (localeScript[0] == '\0');
if (localeScriptWasComputed) {
computeScript();
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 49fe8a261178..6e2ca60cc3d3 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -55,7 +55,9 @@ private:
ZipFileRO::~ZipFileRO() {
CloseArchive(mHandle);
- free(mFileName);
+ if (mFileName != NULL) {
+ free(mFileName);
+ }
}
/*
@@ -76,6 +78,20 @@ ZipFileRO::~ZipFileRO() {
}
+/* static */ ZipFileRO* ZipFileRO::openFd(int fd, const char* debugFileName,
+ bool assume_ownership)
+{
+ ZipArchiveHandle handle;
+ const int32_t error = OpenArchiveFd(fd, debugFileName, &handle, assume_ownership);
+ if (error) {
+ ALOGW("Error opening archive fd %d %s: %s", fd, debugFileName, ErrorCodeString(error));
+ CloseArchive(handle);
+ return NULL;
+ }
+
+ return new ZipFileRO(handle, strdup(debugFileName));
+}
+
ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
{
_ZipEntryRO* data = new _ZipEntryRO;
@@ -139,7 +155,8 @@ bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* su
prefix ? &pe : NULL,
suffix ? &se : NULL);
if (error) {
- ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error));
+ ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
+ ErrorCodeString(error));
delete ze;
return false;
}
@@ -154,7 +171,8 @@ ZipEntryRO ZipFileRO::nextEntry(void* cookie)
int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name));
if (error) {
if (error != -1) {
- ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error));
+ ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>",
+ ErrorCodeString(error));
}
return NULL;
}
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index 0441b9d789e2..4254614c8448 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -92,6 +92,20 @@ public:
bool addOverlayPath(const String8& path, int32_t* cookie);
/*
+ * Add a new source for assets from an already open file descriptor.
+ * This does not give full AssetManager functionality for these assets,
+ * since the origin of the file is not known for purposes of sharing,
+ * overlay resolution, and other features. However it does allow you
+ * to do simple access to the contents of the given fd as an apk file.
+ *
+ * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL,
+ * then on success, *cookie is set to the value corresponding to the
+ * newly-added asset source.
+ */
+ bool addAssetFd(int fd, const String8& debugPathName, int32_t* cookie,
+ bool appAsLib=false, bool assume_ownership=true);
+
+ /*
* Convenience for adding the standard system assets. Uses the
* ANDROID_ROOT environment variable to find them.
*/
@@ -195,15 +209,20 @@ public:
uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize);
private:
+ class SharedZip;
+
struct asset_path
{
- asset_path() : path(""), type(kFileTypeRegular), idmap(""),
- isSystemOverlay(false), isSystemAsset(false) {}
+ asset_path() : path(""), rawFd(-1), type(kFileTypeRegular), idmap(""),
+ isSystemOverlay(false), isSystemAsset(false), assumeOwnership(false) {}
String8 path;
+ int rawFd;
FileType type;
String8 idmap;
bool isSystemOverlay;
bool isSystemAsset;
+ bool assumeOwnership;
+ mutable sp<SharedZip> zip;
};
Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode,
@@ -238,6 +257,7 @@ private:
class SharedZip : public RefBase {
public:
static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true);
+ static sp<SharedZip> create(int fd, const String8& path);
ZipFileRO* getZip();
@@ -257,6 +277,7 @@ private:
private:
SharedZip(const String8& path, time_t modWhen);
+ SharedZip(int fd, const String8& path);
SharedZip(); // <-- not implemented
String8 mPath;
@@ -290,6 +311,8 @@ private:
*/
ZipFileRO* getZip(const String8& path);
+ const sp<SharedZip> getSharedZip(const String8& path);
+
Asset* getZipResourceTableAsset(const String8& path);
Asset* setZipResourceTableAsset(const String8& path, Asset* asset);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 854795559750..20d017813cf7 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1182,6 +1182,10 @@ struct ResTable_config
// tried but could not compute a script.
bool localeScriptWasComputed;
+ // The value of BCP 47 Unicode extension for key 'nu' (numbering system).
+ // Varies in length from 3 to 8 chars. Zero-filled value.
+ char localeNumberingSystem[8];
+
void copyFromDeviceNoSwap(const ResTable_config& o);
void copyFromDtoH(const ResTable_config& o);
@@ -1259,9 +1263,9 @@ struct ResTable_config
// variants, it will be a modified bcp47 tag: b+en+Latn+US.
void appendDirLocale(String8& str) const;
- // Sets the values of language, region, script and variant to the
- // well formed BCP-47 locale contained in |in|. The input locale is
- // assumed to be valid and no validation is performed.
+ // Sets the values of language, region, script, variant and numbering
+ // system to the well formed BCP 47 locale contained in |in|.
+ // The input locale is assumed to be valid and no validation is performed.
void setBcp47Locale(const char* in);
inline void clearLocale() {
@@ -1269,6 +1273,7 @@ struct ResTable_config
localeScriptWasComputed = false;
memset(localeScript, 0, sizeof(localeScript));
memset(localeVariant, 0, sizeof(localeVariant));
+ memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem));
}
inline void computeScript() {
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index 768034287afa..03154d04def1 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -80,6 +80,12 @@ public:
static ZipFileRO* open(const char* zipFileName);
/*
+ * Open an archive from an already open file descriptor.
+ */
+ static ZipFileRO* openFd(int fd, const char* debugFileName,
+ bool assume_ownership = true);
+
+ /*
* Find an entry, by name. Returns the entry identifier, or NULL if
* not found.
*/
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 86a627e1485d..35007c815edd 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -185,6 +185,7 @@ TEST(ConfigLocaleTest, setLocale) {
EXPECT_TRUE(test.localeScriptWasComputed);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
EXPECT_EQ(0, test.localeVariant[0]);
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
test.setBcp47Locale("eng-419");
char out[4] = {1, 1, 1, 1};
@@ -198,6 +199,7 @@ TEST(ConfigLocaleTest, setLocale) {
EXPECT_EQ('4', out[0]);
EXPECT_EQ('1', out[1]);
EXPECT_EQ('9', out[2]);
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
test.setBcp47Locale("en-Latn-419");
EXPECT_EQ('e', test.language[0]);
@@ -209,6 +211,7 @@ TEST(ConfigLocaleTest, setLocale) {
EXPECT_EQ('4', out[0]);
EXPECT_EQ('1', out[1]);
EXPECT_EQ('9', out[2]);
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
test.setBcp47Locale("de-1901");
memset(out, 1, 4);
@@ -222,6 +225,7 @@ TEST(ConfigLocaleTest, setLocale) {
test.unpackRegion(out);
EXPECT_EQ('\0', out[0]);
EXPECT_EQ(0, strcmp("1901", test.localeVariant));
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
test.setBcp47Locale("de-Latn-1901");
memset(out, 1, 4);
@@ -235,6 +239,44 @@ TEST(ConfigLocaleTest, setLocale) {
test.unpackRegion(out);
EXPECT_EQ('\0', out[0]);
EXPECT_EQ(0, strcmp("1901", test.localeVariant));
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
+
+ test.setBcp47Locale("ar-EG-u-nu-latn");
+ EXPECT_EQ('a', test.language[0]);
+ EXPECT_EQ('r', test.language[1]);
+ EXPECT_EQ('E', test.country[0]);
+ EXPECT_EQ('G', test.country[1]);
+ EXPECT_TRUE(test.localeScriptWasComputed);
+ EXPECT_EQ(0, memcmp("Arab", test.localeScript, 4));
+ EXPECT_EQ(0, test.localeVariant[0]);
+ EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4));
+
+ test.setBcp47Locale("ar-EG-u");
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
+
+ test.setBcp47Locale("ar-EG-u-nu");
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
+
+ test.setBcp47Locale("ar-EG-u-attr-nu-latn");
+ EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4));
+
+ test.setBcp47Locale("ar-EG-u-ca-gregory-nu-latn");
+ EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4));
+
+ test.setBcp47Locale("ar-EG-u-nu-latn-ca-gregory");
+ EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4));
+
+ test.setBcp47Locale("ar-EG-u-nu-toolongnumsys");
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
+
+ test.setBcp47Locale("ar-EG-u-nu-latn-nu-arab");
+ EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4));
+
+ test.setBcp47Locale("ar-EG-u-co-nu-latn");
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
+
+ test.setBcp47Locale("ar-u-co-abcd-attr-nu-latn");
+ EXPECT_EQ(0, test.localeNumberingSystem[0]);
}
TEST(ConfigLocaleTest, computeScript) {
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 6b8006cc5a33..0919e82a003b 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -31,10 +31,11 @@ namespace android {
namespace uirenderer {
Extensions::Extensions() {
- if (Properties::getRenderPipelineType() != RenderPipelineType::OpenGL) {
- //Extensions class is used only by OpenGL pipeline
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ //Extensions class is used only by OpenGL and SkiaGL pipelines
//The code below will crash for SkiaVulkan, because OpenGL is not initialized
//TODO: instantiate Extensions class only for OpenGL pipeline
+ //TODO: remove the only usage of Extensions by SkiaGL in SkiaOpenGLReadback::copyImageInto
return;
}
const char* version = (const char*) glGetString(GL_VERSION);
@@ -65,6 +66,7 @@ Extensions::Extensions() {
mHas1BitStencil = extensions.has("GL_OES_stencil1");
mHas4BitStencil = extensions.has("GL_OES_stencil4");
mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
+ mHasRenderableFloatTexture = extensions.has("GL_OES_texture_half_float");
mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB");
mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control");
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 0ecfdb1b3e0a..7af7f7944ac9 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -38,6 +38,9 @@ public:
inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
+ inline bool hasRenderableFloatTextures() const {
+ return (mVersionMajor >= 3 && mVersionMinor >= 2) || mHasRenderableFloatTexture;
+ }
inline bool hasSRGB() const { return mHasSRGB; }
inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; }
inline bool hasLinearBlending() const { return hasSRGB() && mHasLinearBlending; }
@@ -56,6 +59,7 @@ private:
bool mHasSRGB;
bool mHasSRGBWriteControl;
bool mHasLinearBlending;
+ bool mHasRenderableFloatTexture;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 2687410897ac..751e2037db91 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -128,7 +128,8 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
return CopyResult::DestinationInvalid;
}
- if (bitmap->colorType() == kRGBA_F16_SkColorType && !caches.extensions().hasFloatTextures()) {
+ if (bitmap->colorType() == kRGBA_F16_SkColorType &&
+ !caches.extensions().hasRenderableFloatTextures()) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 75967e9f6503..049018cc321c 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -73,7 +73,7 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4
* for reading back float buffers (skbug.com/6945).
*/
if (pixelConfig == kRGBA_half_GrPixelConfig &&
- !grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
+ !DeviceInfo::get()->extensions().hasRenderableFloatTextures()) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
@@ -84,57 +84,50 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4
sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
kTopLeft_GrSurfaceOrigin));
if (image) {
- // Convert imgTransform matrix from right to left handed coordinate system.
- // If we have a matrix transformation in right handed coordinate system
- //|ScaleX, SkewX, TransX| same transform in left handed is |ScaleX, SkewX, TransX |
- //|SkewY, ScaleY, TransY| |-SkewY, -ScaleY, 1-TransY|
- //|0, 0, 1 | |0, 0, 1 |
- SkMatrix textureMatrix;
- textureMatrix.setIdentity();
- textureMatrix[SkMatrix::kMScaleX] = imgTransform[Matrix4::kScaleX];
- textureMatrix[SkMatrix::kMScaleY] = -imgTransform[Matrix4::kScaleY];
- textureMatrix[SkMatrix::kMSkewX] = imgTransform[Matrix4::kSkewX];
- textureMatrix[SkMatrix::kMSkewY] = -imgTransform[Matrix4::kSkewY];
- textureMatrix[SkMatrix::kMTransX] = imgTransform[Matrix4::kTranslateX];
- textureMatrix[SkMatrix::kMTransY] = 1-imgTransform[Matrix4::kTranslateY];
-
- // textureMatrix maps 2D texture coordinates of the form (s, t, 1) with s and t in the
- // inclusive range [0, 1] to the texture (see GLConsumer::getTransformMatrix comments).
- // Convert textureMatrix to translate in real texture dimensions. Texture width and
- // height are affected by the orientation (width and height swapped for 90/270 rotation).
- if (textureMatrix[SkMatrix::kMSkewX] >= 0.5f || textureMatrix[SkMatrix::kMSkewX] <= -0.5f) {
- textureMatrix[SkMatrix::kMTransX] *= imgHeight;
- textureMatrix[SkMatrix::kMTransY] *= imgWidth;
- } else {
- textureMatrix[SkMatrix::kMTransX] *= imgWidth;
- textureMatrix[SkMatrix::kMTransY] *= imgHeight;
+ int displayedWidth = imgWidth, displayedHeight = imgHeight;
+ // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
+ // size.
+ if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
+ std::swap(displayedWidth, displayedHeight);
}
-
- // convert to Skia data structures
- SkRect skiaSrcRect = srcRect.toSkRect();
- SkMatrix textureMatrixInv;
SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
- bool srcNotEmpty = false;
- if (textureMatrix.invert(&textureMatrixInv)) {
- if (skiaSrcRect.isEmpty()) {
- skiaSrcRect = SkRect::MakeIWH(imgWidth, imgHeight);
- srcNotEmpty = !skiaSrcRect.isEmpty();
- } else {
- // src and dest rectangles need to be converted into texture coordinates before the
- // rotation matrix is applied (because drawImageRect preconcat its matrix).
- textureMatrixInv.mapRect(&skiaSrcRect);
- srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(imgWidth, imgHeight));
- }
- textureMatrixInv.mapRect(&skiaDestRect);
+ SkRect skiaSrcRect = srcRect.toSkRect();
+ if (skiaSrcRect.isEmpty()) {
+ skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
}
+ bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
if (srcNotEmpty) {
+ SkMatrix textureMatrixInv;
+ imgTransform.copyTo(textureMatrixInv);
+ //TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
+ // use bottom left origin and remove flipV and invert transformations.
+ SkMatrix flipV;
+ flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
+ textureMatrixInv.preConcat(flipV);
+ textureMatrixInv.preScale(1.0f/displayedWidth, 1.0f/displayedHeight);
+ textureMatrixInv.postScale(imgWidth, imgHeight);
+ SkMatrix textureMatrix;
+ if (!textureMatrixInv.invert(&textureMatrix)) {
+ textureMatrix = textureMatrixInv;
+ }
+
+ textureMatrixInv.mapRect(&skiaSrcRect);
+ textureMatrixInv.mapRect(&skiaDestRect);
+
// we render in an offscreen buffer to scale and to avoid an issue b/62262733
// with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
grContext.get(), SkBudgeted::kYes, bitmap->info());
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
+ // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
+ // is codified by tests using golden images like DecodeAccuracyTest.
+ if (skiaSrcRect.width() != bitmap->width()
+ || skiaSrcRect.height() != bitmap->height()) {
+ //TODO: apply filter always, but check if tests will be fine
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ }
scaledSurface->getCanvas()->concat(textureMatrix);
scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
SkCanvas::kFast_SrcRectConstraint);
diff --git a/libs/hwui/tests/scripts/prep_taieye.sh b/libs/hwui/tests/scripts/prep_taieye.sh
new file mode 100755
index 000000000000..503f6d5d5239
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_taieye.sh
@@ -0,0 +1,50 @@
+nr=$(adb shell cat /proc/cpuinfo | grep processor | wc -l)
+cpubase=/sys/devices/system/cpu
+
+adb root
+adb wait-for-device
+adb shell stop vendor.perfd
+adb shell stop thermal-engine
+
+S=1036800
+cpu=0
+# Changing governor and frequency in one core will be automatically applied
+# to other cores in the cluster
+while [ $((cpu < 4)) -eq 1 ]; do
+ echo "Setting cpu ${cpu} to $S hz"
+ adb shell "echo userspace > $cpubase/cpu${cpu}/cpufreq/scaling_governor"
+ adb shell "echo 1 > $cpubase/cpu${cpu}/online"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq"
+ cpu=$(($cpu + 1))
+done
+
+while [ $((cpu < $nr)) -eq 1 ]; do
+ echo "disable cpu $cpu"
+ adb shell "echo 0 > $cpubase/cpu${cpu}/online"
+ cpu=$(($cpu + 1))
+done
+
+echo "setting GPU bus and idle timer"
+adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split"
+adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
+adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
+
+#0 762 1144 1525 2288 3143 4173 5195 5859 7759 9887 11863 13763
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,gpubw/min_freq"
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,gpubw/max_freq"
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,cpubw/min_freq"
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,cpubw/max_freq"
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,mincpubw/min_freq"
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,mincpubw/max_freq"
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,memlat-cpu0/min_freq"
+adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,memlat-cpu0/max_freq"
+
+# 180000000 257000000 342000000 414000000 515000000 596000000 670000000 710000000
+echo "performance mode, 342 MHz"
+adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
+adb shell "echo 342000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq"
+adb shell "echo 342000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq"
+
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel"
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel"
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
new file mode 100644
index 000000000000..4f1d2d5a4fe5
--- /dev/null
+++ b/libs/protoutil/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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.
+
+cc_library {
+ name: "libprotoutil",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+
+ srcs: [
+ "src/EncodedBuffer.cpp",
+ "src/ProtoOutputStream.cpp",
+ "src/protobuf.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+}
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk
deleted file mode 100644
index 2a2b087dc032..000000000000
--- a/libs/protoutil/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libprotoutil
-
-LOCAL_CFLAGS := \
- -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
- src/EncodedBuffer.cpp \
- src/ProtoOutputStream.cpp \
- src/protobuf.cpp \
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 10be6499417f..ce4184967026 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -19,13 +19,63 @@
#include <android/util/EncodedBuffer.h>
-#include <stdint.h>
#include <string>
namespace android {
namespace util {
/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+/**
+ * Mask for the field types stored in a fieldId. Leaves a whole
+ * byte for future expansion, even though there are currently only 17 types.
+ */
+const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
+
+/**
+ * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly,
+ * so no extra mapping needs to be maintained in this case.
+ */
+const uint64_t FIELD_TYPE_UNKNOWN = 0;
+const uint64_t FIELD_TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
+const uint64_t FIELD_TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
+const uint64_t FIELD_TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT64 if negative
+ // values are likely.
+const uint64_t FIELD_TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
+const uint64_t FIELD_TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT32 if negative
+ // values are likely.
+const uint64_t FIELD_TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
+const uint64_t FIELD_TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
+const uint64_t FIELD_TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
+const uint64_t FIELD_TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
+// const uint64_t FIELD_TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
+const uint64_t FIELD_TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
+
+const uint64_t FIELD_TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
+const uint64_t FIELD_TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
+const uint64_t FIELD_TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
+const uint64_t FIELD_TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
+const uint64_t FIELD_TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
+const uint64_t FIELD_TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
+const uint64_t FIELD_TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 5ULL << FIELD_COUNT_SHIFT;
+
+/**
* Class to write to a protobuf stream.
*
* Each write method takes an ID code from the protoc generated classes
@@ -60,13 +110,14 @@ public:
void end(long long token);
/**
- * Flushes the protobuf data out to given fd.
+ * Flushes the protobuf data out to given fd. When the following functions are called,
+ * it is not able to write to ProtoOutputStream any more since the data is compact.
*/
- size_t size();
- EncodedBuffer::iterator data();
- bool flush(int fd);
+ size_t size(); // Get the size of the serialized protobuf.
+ EncodedBuffer::iterator data(); // Get the reader apis of the data.
+ bool flush(int fd); // Flush data directly to a file descriptor.
- // Please don't use the following functions to dump protos unless you are sure about it.
+ // Please don't use the following functions to dump protos unless you are familiar with protobuf encoding.
void writeRawVarint(uint64_t varint);
void writeLengthDelimitedHeader(uint32_t id, size_t size);
void writeRawByte(uint8_t byte);
@@ -94,6 +145,7 @@ private:
inline void writeEnumImpl(uint32_t id, int val);
inline void writeBoolImpl(uint32_t id, bool val);
inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size);
+ inline void writeMessageBytesImpl(uint32_t id, const char* val, size_t size);
bool compact();
size_t editEncodedSize(size_t rawSize);
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 9dadf1c20510..9d8ee729a80d 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -18,58 +18,10 @@
#include <android/util/protobuf.h>
#include <android/util/ProtoOutputStream.h>
#include <cutils/log.h>
-#include <cstring>
namespace android {
namespace util {
-/**
- * Position of the field type in a (long long) fieldId.
- */
-const uint64_t FIELD_TYPE_SHIFT = 32;
-
-/**
- * Mask for the field types stored in a fieldId. Leaves a whole
- * byte for future expansion, even though there are currently only 17 types.
- */
-const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
-
-const uint64_t FIELD_TYPE_UNKNOWN = 0;
-const uint64_t TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
-const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
-const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
- // take 10 bytes. Use TYPE_SINT64 if negative
- // values are likely.
-const uint64_t TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
-const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
- // take 10 bytes. Use TYPE_SINT32 if negative
- // values are likely.
-const uint64_t TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
-const uint64_t TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
-const uint64_t TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
-const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
-const uint64_t TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
-const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
-
-const uint64_t TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
-const uint64_t TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
-const uint64_t TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
-const uint64_t TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
-const uint64_t TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
-const uint64_t TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
-const uint64_t TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
-
-//
-// FieldId flags for whether the field is single, repeated or packed.
-// TODO: packed is not supported yet.
-//
-const uint64_t FIELD_COUNT_SHIFT = 40;
-const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_UNKNOWN = 0;
-const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
-
ProtoOutputStream::ProtoOutputStream()
:mBuffer(),
mCopyBegin(0),
@@ -90,18 +42,18 @@ ProtoOutputStream::write(uint64_t fieldId, double val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
default:
ALOGW("Field type %d is not supported when writing double val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -116,18 +68,18 @@ ProtoOutputStream::write(uint64_t fieldId, float val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
default:
ALOGW("Field type %d is not supported when writing float val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -142,20 +94,20 @@ ProtoOutputStream::write(uint64_t fieldId, int val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
default:
ALOGW("Field type %d is not supported when writing int val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -170,20 +122,20 @@ ProtoOutputStream::write(uint64_t fieldId, long long val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
default:
ALOGW("Field type %d is not supported when writing long long val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -198,7 +150,7 @@ ProtoOutputStream::write(uint64_t fieldId, bool val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_BOOL:
+ case FIELD_TYPE_BOOL:
writeBoolImpl(id, val);
return true;
default:
@@ -214,7 +166,7 @@ ProtoOutputStream::write(uint64_t fieldId, string val)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_STRING:
+ case FIELD_TYPE_STRING:
writeUtf8StringImpl(id, val.c_str(), val.size());
return true;
default:
@@ -230,10 +182,14 @@ ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size)
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_STRING:
- case TYPE_BYTES:
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_BYTES:
writeUtf8StringImpl(id, val, size);
return true;
+ case FIELD_TYPE_MESSAGE:
+ // can directly write valid format of message bytes into ProtoOutputStream without calling start/end
+ writeMessageBytesImpl(id, val, size);
+ return true;
default:
ALOGW("Field type %d is not supported when writing char[] val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -287,7 +243,7 @@ static int getSizePosFromToken(long long token) {
long long
ProtoOutputStream::start(uint64_t fieldId)
{
- if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) {
+ if ((fieldId & FIELD_TYPE_MASK) != FIELD_TYPE_MESSAGE) {
ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId);
return 0;
}
@@ -560,7 +516,6 @@ inline To bit_cast(From const &from) {
inline void
ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
{
- if (val == 0.0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val));
}
@@ -568,7 +523,6 @@ ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
inline void
ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
{
- if (val == 0.0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val));
}
@@ -576,7 +530,6 @@ ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
inline void
ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64((uint64_t)val);
}
@@ -584,7 +537,6 @@ ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
inline void
ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32((uint32_t)val);
}
@@ -592,7 +544,6 @@ ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
inline void
ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64(val);
}
@@ -600,7 +551,6 @@ ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
inline void
ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32(val);
}
@@ -608,7 +558,6 @@ ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
inline void
ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64(val);
}
@@ -616,7 +565,6 @@ ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
inline void
ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32(val);
}
@@ -624,7 +572,6 @@ ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
inline void
ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64((uint64_t)val);
}
@@ -632,7 +579,6 @@ ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
inline void
ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32((uint32_t)val);
}
@@ -640,7 +586,6 @@ ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
inline void
ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
}
@@ -648,7 +593,6 @@ ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
inline void
ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
}
@@ -663,7 +607,6 @@ ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
inline void
ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
{
- if (!val) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32(val ? 1 : 0);
}
@@ -671,13 +614,23 @@ ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
inline void
ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
{
- if (val == NULL || size == 0) return;
+ if (val == NULL) return;
writeLengthDelimitedHeader(id, size);
for (size_t i=0; i<size; i++) {
mBuffer.writeRawByte((uint8_t)val[i]);
}
}
+inline void
+ProtoOutputStream::writeMessageBytesImpl(uint32_t id, const char* val, size_t size)
+{
+ if (val == NULL) return;
+ writeLengthDelimitedHeader(id, size);
+ for (size_t i=0; i<size; i++) {
+ mBuffer.writeRawByte(val[i]);
+ }
+}
+
} // util
} // android
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index e7f903e831ff..c9d2f7f8bfd4 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -821,7 +821,7 @@ public class Location implements Parcelable {
* considered 1 standard deviation.
*
* <p>For example, if {@link #getAltitude()} returns 150, and
- * {@link #getVerticalAccuracyMeters()} ()} returns 20 then there is a 68% probability
+ * {@link #getVerticalAccuracyMeters()} returns 20 then there is a 68% probability
* of the true altitude being between 130 and 170 meters.
*
* <p>If this location does not have a vertical accuracy, then 0.0 is returned.
@@ -933,7 +933,7 @@ public class Location implements Parcelable {
* considered 1 standard deviation.
*
* <p>For example, if {@link #getBearing()} returns 60, and
- * {@link #getBearingAccuracyDegrees()} ()} returns 10, then there is a 68% probability of the
+ * {@link #getBearingAccuracyDegrees()} returns 10, then there is a 68% probability of the
* true bearing being between 50 and 70 degrees.
*
* <p>If this location does not have a bearing accuracy, then 0.0 is returned.
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index ba41a7bd0868..91754162180f 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2564,51 +2564,66 @@ public class ExifInterface {
});
}
+ String hasImage = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
String hasVideo = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
- final String METADATA_HAS_VIDEO_VALUE_YES = "yes";
- if (METADATA_HAS_VIDEO_VALUE_YES.equals(hasVideo)) {
- String width = retriever.extractMetadata(
+ String width = null;
+ String height = null;
+ String rotation = null;
+ final String METADATA_VALUE_YES = "yes";
+ // If the file has both image and video, prefer image info over video info.
+ // App querying ExifInterface is most likely using the bitmap path which
+ // picks the image first.
+ if (METADATA_VALUE_YES.equals(hasImage)) {
+ width = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
+ height = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
+ rotation = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
+ } else if (METADATA_VALUE_YES.equals(hasVideo)) {
+ width = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
- String height = retriever.extractMetadata(
+ height = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ rotation = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ }
- if (width != null) {
- mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
- ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
- }
-
- if (height != null) {
- mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
- ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
- }
+ if (width != null) {
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
+ ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
+ }
- String rotation = retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
- if (rotation != null) {
- int orientation = ExifInterface.ORIENTATION_NORMAL;
+ if (height != null) {
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
+ ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
+ }
- // all rotation angles in CW
- switch (Integer.parseInt(rotation)) {
- case 90:
- orientation = ExifInterface.ORIENTATION_ROTATE_90;
- break;
- case 180:
- orientation = ExifInterface.ORIENTATION_ROTATE_180;
- break;
- case 270:
- orientation = ExifInterface.ORIENTATION_ROTATE_270;
- break;
- }
+ if (rotation != null) {
+ int orientation = ExifInterface.ORIENTATION_NORMAL;
- mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
- ExifAttribute.createUShort(orientation, mExifByteOrder));
+ // all rotation angles in CW
+ switch (Integer.parseInt(rotation)) {
+ case 90:
+ orientation = ExifInterface.ORIENTATION_ROTATE_90;
+ break;
+ case 180:
+ orientation = ExifInterface.ORIENTATION_ROTATE_180;
+ break;
+ case 270:
+ orientation = ExifInterface.ORIENTATION_ROTATE_270;
+ break;
}
- if (DEBUG) {
- Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
- }
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
+ ExifAttribute.createUShort(orientation, mExifByteOrder));
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
}
} finally {
retriever.release();
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index ed5f7d848663..c475e122833a 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -96,6 +96,19 @@ import java.util.Map;
* <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr>
* <tr><td>{@link #KEY_LANGUAGE}</td><td>String</td><td>The language of the content.</td></tr>
* </table>
+ *
+ * Image formats have the following keys:
+ * <table>
+ * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr>
+ * <tr><td>{@link #KEY_WIDTH}</td><td>Integer</td><td></td></tr>
+ * <tr><td>{@link #KEY_HEIGHT}</td><td>Integer</td><td></td></tr>
+ * <tr><td>{@link #KEY_COLOR_FORMAT}</td><td>Integer</td><td>set by the user
+ * for encoders, readable in the output format of decoders</b></td></tr>
+ * <tr><td>{@link #KEY_GRID_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_GRID_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_GRID_ROWS}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_GRID_COLS}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * </table>
*/
public final class MediaFormat {
public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
@@ -126,6 +139,35 @@ public final class MediaFormat {
public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
/**
+ * MIME type for HEIF still image data encoded in HEVC.
+ *
+ * To decode such an image, {@link MediaCodec} decoder for
+ * {@ #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
+ * the correct {@link #MediaFormat} based on additional information in
+ * the track format, and send it to {@link MediaCodec#configure}.
+ *
+ * The track's MediaFormat will come with {@link #KEY_WIDTH} and
+ * {@link #KEY_HEIGHT} keys, which describes the width and height
+ * of the image. If the image doesn't contain grid (i.e. none of
+ * {@link #KEY_GRID_WIDTH}, {@link #KEY_GRID_HEIGHT},
+ * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLS} are present}), the
+ * track will contain a single sample of coded data for the entire image,
+ * and the image width and height should be used to set up the decoder.
+ *
+ * If the image does come with grid, each sample from the track will
+ * contain one tile in the grid, of which the size is described by
+ * {@link #KEY_GRID_WIDTH} and {@link #KEY_GRID_HEIGHT}. This size
+ * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be
+ * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS}
+ * by {@link #KEY_GRID_COLS} samples in row-major, top-row first,
+ * left-to-right order. The output image should be reconstructed by
+ * first tiling the decoding results of the tiles in the correct order,
+ * then trimming (before rotation is applied) on the bottom and right
+ * side, if the tiled area is larger than the image width and height.
+ */
+ public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
+
+ /**
* MIME type for WebVTT subtitle data.
*/
public static final String MIMETYPE_TEXT_VTT = "text/vtt";
@@ -232,6 +274,54 @@ public final class MediaFormat {
public static final String KEY_FRAME_RATE = "frame-rate";
/**
+ * A key describing the grid width of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
+ * track. The associated value is an integer.
+ *
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ *
+ * @see #KEY_GRID_HEIGHT
+ * @see #KEY_GRID_ROWS
+ * @see #KEY_GRID_COLS
+ */
+ public static final String KEY_GRID_WIDTH = "grid-width";
+
+ /**
+ * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
+ * track. The associated value is an integer.
+ *
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ *
+ * @see #KEY_GRID_WIDTH
+ * @see #KEY_GRID_ROWS
+ * @see #KEY_GRID_COLS
+ */
+ public static final String KEY_GRID_HEIGHT = "grid-height";
+
+ /**
+ * A key describing the number of grid rows in the content in a
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ *
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ *
+ * @see #KEY_GRID_WIDTH
+ * @see #KEY_GRID_HEIGHT
+ * @see #KEY_GRID_COLS
+ */
+ public static final String KEY_GRID_ROWS = "grid-rows";
+
+ /**
+ * A key describing the number of grid columns in the content in a
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ *
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ *
+ * @see #KEY_GRID_WIDTH
+ * @see #KEY_GRID_HEIGHT
+ * @see #KEY_GRID_ROWS
+ */
+ public static final String KEY_GRID_COLS = "grid-cols";
+
+ /**
* A key describing the raw audio sample encoding/format.
*
* <p>The associated value is an integer, using one of the
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 760cc49bc1e2..0b8640184ff5 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -47,7 +47,7 @@ public class MediaMetadataRetriever
// The field below is accessed by native methods
@SuppressWarnings("unused")
private long mNativeContext;
-
+
private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF;
public MediaMetadataRetriever() {
@@ -58,7 +58,7 @@ public class MediaMetadataRetriever
* Sets the data source (file pathname) to use. Call this
* method before the rest of the methods in this class. This method may be
* time-consuming.
- *
+ *
* @param path The path of the input media file.
* @throws IllegalArgumentException If the path is invalid.
*/
@@ -113,7 +113,7 @@ public class MediaMetadataRetriever
* responsibility to close the file descriptor. It is safe to do so as soon
* as this call returns. Call this method before the rest of the methods in
* this class. This method may be time-consuming.
- *
+ *
* @param fd the FileDescriptor for the file you want to play
* @param offset the offset into the file where the data to be played starts,
* in bytes. It must be non-negative
@@ -123,13 +123,13 @@ public class MediaMetadataRetriever
*/
public native void setDataSource(FileDescriptor fd, long offset, long length)
throws IllegalArgumentException;
-
+
/**
* Sets the data source (FileDescriptor) to use. It is the caller's
* responsibility to close the file descriptor. It is safe to do so as soon
* as this call returns. Call this method before the rest of the methods in
* this class. This method may be time-consuming.
- *
+ *
* @param fd the FileDescriptor for the file you want to play
* @throws IllegalArgumentException if the FileDescriptor is invalid
*/
@@ -138,11 +138,11 @@ public class MediaMetadataRetriever
// intentionally less than LONG_MAX
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
-
+
/**
- * Sets the data source as a content Uri. Call this method before
+ * Sets the data source as a content Uri. Call this method before
* the rest of the methods in this class. This method may be time-consuming.
- *
+ *
* @param context the Context to use when resolving the Uri
* @param uri the Content URI of the data you want to play
* @throws IllegalArgumentException if the Uri is invalid
@@ -154,7 +154,7 @@ public class MediaMetadataRetriever
if (uri == null) {
throw new IllegalArgumentException();
}
-
+
String scheme = uri.getScheme();
if(scheme == null || scheme.equals("file")) {
setDataSource(uri.getPath());
@@ -213,12 +213,12 @@ public class MediaMetadataRetriever
/**
* Call this method after setDataSource(). This method retrieves the
* meta data value associated with the keyCode.
- *
+ *
* The keyCode currently supported is listed below as METADATA_XXX
* constants. With any other value, it returns a null pointer.
- *
+ *
* @param keyCode One of the constants listed below at the end of the class.
- * @return The meta data value associate with the given keyCode on success;
+ * @return The meta data value associate with the given keyCode on success;
* null on failure.
*/
public native String extractMetadata(int keyCode);
@@ -357,6 +357,109 @@ public class MediaMetadataRetriever
private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
/**
+ * This method retrieves a video frame by its index. It should only be called
+ * after {@link #setDataSource}.
+ *
+ * @param frameIndex 0-based index of the video frame. The frame index must be that of
+ * a valid frame. The total number of frames available for retrieval can be queried
+ * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+ *
+ * @throws IllegalStateException if the container doesn't contain video or image sequences.
+ * @throws IllegalArgumentException if the requested frame index does not exist.
+ *
+ * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
+ *
+ * @see #getFramesAtIndex(int, int)
+ */
+ public Bitmap getFrameAtIndex(int frameIndex) {
+ Bitmap[] bitmaps = getFramesAtIndex(frameIndex, 1);
+ if (bitmaps == null || bitmaps.length < 1) {
+ return null;
+ }
+ return bitmaps[0];
+ }
+
+ /**
+ * This method retrieves a consecutive set of video frames starting at the
+ * specified index. It should only be called after {@link #setDataSource}.
+ *
+ * If the caller intends to retrieve more than one consecutive video frames,
+ * this method is preferred over {@link #getFrameAtIndex(int)} for efficiency.
+ *
+ * @param frameIndex 0-based index of the first video frame to retrieve. The frame index
+ * must be that of a valid frame. The total number of frames available for retrieval
+ * can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+ * @param numFrames number of consecutive video frames to retrieve. Must be a positive
+ * value. The stream must contain at least numFrames frames starting at frameIndex.
+ *
+ * @throws IllegalStateException if the container doesn't contain video or image sequences.
+ * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
+ * stream doesn't contain at least numFrames starting at frameIndex.
+
+ * @return An array of Bitmaps containing the requested video frames. The returned
+ * array could contain less frames than requested if the retrieval fails.
+ *
+ * @see #getFrameAtIndex(int)
+ */
+ public Bitmap[] getFramesAtIndex(int frameIndex, int numFrames) {
+ if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
+ throw new IllegalStateException("Does not contail video or image sequences");
+ }
+ int frameCount = Integer.parseInt(
+ extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
+ if (frameIndex < 0 || numFrames < 1
+ || frameIndex >= frameCount
+ || frameIndex > frameCount - numFrames) {
+ throw new IllegalArgumentException("Invalid frameIndex or numFrames: "
+ + frameIndex + ", " + numFrames);
+ }
+ return _getFrameAtIndex(frameIndex, numFrames);
+ }
+ private native Bitmap[] _getFrameAtIndex(int frameIndex, int numFrames);
+
+ /**
+ * This method retrieves a still image by its index. It should only be called
+ * after {@link #setDataSource}.
+ *
+ * @param imageIndex 0-based index of the image, with negative value indicating
+ * the primary image.
+ * @throws IllegalStateException if the container doesn't contain still images.
+ * @throws IllegalArgumentException if the requested image does not exist.
+ *
+ * @return the requested still image, or null if the image cannot be retrieved.
+ *
+ * @see #getPrimaryImage
+ */
+ public Bitmap getImageAtIndex(int imageIndex) {
+ if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
+ throw new IllegalStateException("Does not contail still images");
+ }
+
+ String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
+ if (imageIndex >= Integer.parseInt(imageCount)) {
+ throw new IllegalArgumentException("Invalid image index: " + imageCount);
+ }
+
+ return _getImageAtIndex(imageIndex);
+ }
+
+ /**
+ * This method retrieves the primary image of the media content. It should only
+ * be called after {@link #setDataSource}.
+ *
+ * @return the primary image, or null if it cannot be retrieved.
+ *
+ * @throws IllegalStateException if the container doesn't contain still images.
+ *
+ * @see #getImageAtIndex(int)
+ */
+ public Bitmap getPrimaryImage() {
+ return getImageAtIndex(-1);
+ }
+
+ private native Bitmap _getImageAtIndex(int imageIndex);
+
+ /**
* Call this method after setDataSource(). This method finds the optional
* graphic or album/cover art associated associated with the data source. If
* there are more than one pictures, (any) one of them is returned.
@@ -572,5 +675,40 @@ public class MediaMetadataRetriever
* number.
*/
public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25;
+ /**
+ * If this key exists the media contains still image content.
+ */
+ public static final int METADATA_KEY_HAS_IMAGE = 26;
+ /**
+ * If the media contains still images, this key retrieves the number
+ * of still images.
+ */
+ public static final int METADATA_KEY_IMAGE_COUNT = 27;
+ /**
+ * If the media contains still images, this key retrieves the image
+ * index of the primary image.
+ */
+ public static final int METADATA_KEY_IMAGE_PRIMARY = 28;
+ /**
+ * If the media contains still images, this key retrieves the width
+ * of the primary image.
+ */
+ public static final int METADATA_KEY_IMAGE_WIDTH = 29;
+ /**
+ * If the media contains still images, this key retrieves the height
+ * of the primary image.
+ */
+ public static final int METADATA_KEY_IMAGE_HEIGHT = 30;
+ /**
+ * If the media contains still images, this key retrieves the rotation
+ * of the primary image.
+ */
+ public static final int METADATA_KEY_IMAGE_ROTATION = 31;
+ /**
+ * If the media contains video and this key exists, it retrieves the
+ * total number of frames in the video sequence.
+ */
+ public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32;
+
// Add more here...
}
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 4659ae131f53..42ebf6ab8cfc 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -244,29 +244,9 @@ static void rotate(T *dst, const T *src, size_t width, size_t height, int angle)
}
}
-static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
- JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
-{
- ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
- (long long)timeUs, option, dst_width, dst_height);
- MediaMetadataRetriever* retriever = getRetriever(env, thiz);
- if (retriever == 0) {
- jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
- return NULL;
- }
-
- // Call native method to retrieve a video frame
- VideoFrame *videoFrame = NULL;
- sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
- if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
- videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
- }
- if (videoFrame == NULL) {
- ALOGE("getFrameAtTime: videoFrame is a NULL pointer");
- return NULL;
- }
-
- ALOGV("Dimension = %dx%d and bytes = %d",
+static jobject getBitmapFromVideoFrame(
+ JNIEnv *env, VideoFrame *videoFrame, jint dst_width, jint dst_height) {
+ ALOGV("getBitmapFromVideoFrame: dimension = %dx%d and bytes = %d",
videoFrame->mDisplayWidth,
videoFrame->mDisplayHeight,
videoFrame->mSize);
@@ -301,7 +281,7 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
if (env->ExceptionCheck()) {
env->ExceptionClear();
}
- ALOGE("getFrameAtTime: create Bitmap failed!");
+ ALOGE("getBitmapFromVideoFrame: create Bitmap failed!");
return NULL;
}
@@ -340,6 +320,93 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
return jBitmap;
}
+static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
+ JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
+{
+ ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
+ (long long)timeUs, option, dst_width, dst_height);
+ MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+ if (retriever == 0) {
+ jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
+ return NULL;
+ }
+
+ // Call native method to retrieve a video frame
+ VideoFrame *videoFrame = NULL;
+ sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
+ if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
+ videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ }
+ if (videoFrame == NULL) {
+ ALOGE("getFrameAtTime: videoFrame is a NULL pointer");
+ return NULL;
+ }
+
+ return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height);
+}
+
+static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
+ JNIEnv *env, jobject thiz, jint index)
+{
+ ALOGV("getImageAtIndex: index %d", index);
+ MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+ if (retriever == 0) {
+ jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
+ return NULL;
+ }
+
+ // Call native method to retrieve an image
+ VideoFrame *videoFrame = NULL;
+ sp<IMemory> frameMemory = retriever->getImageAtIndex(index);
+ if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
+ videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
+ }
+ if (videoFrame == NULL) {
+ ALOGE("getImageAtIndex: videoFrame is a NULL pointer");
+ return NULL;
+ }
+
+ return getBitmapFromVideoFrame(env, videoFrame, -1, -1);
+}
+
+static jobjectArray android_media_MediaMetadataRetriever_getFrameAtIndex(
+ JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames)
+{
+ ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d", frameIndex, numFrames);
+ MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+ if (retriever == 0) {
+ jniThrowException(env,
+ "java/lang/IllegalStateException", "No retriever available");
+ return NULL;
+ }
+
+ std::vector<sp<IMemory> > frames;
+ status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames);
+ if (err != OK || frames.size() == 0) {
+ ALOGE("failed to get frames from retriever, err=%d, size=%zu",
+ err, frames.size());
+ return NULL;
+ }
+
+ jobjectArray bitmapArrayObj = env->NewObjectArray(
+ frames.size(), fields.bitmapClazz, NULL);
+ if (bitmapArrayObj == NULL) {
+ ALOGE("can't create bitmap array object");
+ return NULL;
+ }
+
+ for (size_t i = 0; i < frames.size(); i++) {
+ if (frames[i] == NULL || frames[i]->pointer() == NULL) {
+ ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i);
+ continue;
+ }
+ VideoFrame *videoFrame = static_cast<VideoFrame *>(frames[i]->pointer());
+ jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1);
+ env->SetObjectArrayElement(bitmapArrayObj, i, bitmapObj);
+ }
+ return bitmapArrayObj;
+}
+
static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
JNIEnv *env, jobject thiz, jint pictureType)
{
@@ -485,6 +552,8 @@ static const JNINativeMethod nativeMethods[] = {
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
{"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
+ {"_getImageAtIndex", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getImageAtIndex},
+ {"_getFrameAtIndex", "(II)[Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtIndex},
{"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
{"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
{"release", "()V", (void *)android_media_MediaMetadataRetriever_release},
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 0da628917713..fe2a939b58a6 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -955,6 +955,7 @@ void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize)
outThumbSize = image_data.thumbnail.length;
} else {
free(result);
+ result = NULL;
}
}
break;
diff --git a/packages/SettingsLib/res/drawable/ic_bt_laptop.xml b/packages/SettingsLib/res/drawable/ic_bt_laptop.xml
new file mode 100644
index 000000000000..029e4d913434
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_laptop.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,18c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0
+ -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml b/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml
new file mode 100644
index 000000000000..6e32e1a7f631
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_settings_print.xml b/packages/SettingsLib/res/drawable/ic_settings_print.xml
new file mode 100644
index 000000000000..0eab4021956b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_settings_print.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M19,8H5c-1.66,0-3,1.34-3,3v5c0,0.55,0.45,1,1,1h3v3c0,0.55,0.45,1,1,1h10c0.55,0,1-0.45,1-1v-3h3c0.55,0,1-0.45,1-1v-5
+C22,9.34,20.66,8,19,8z M16,19H8v-5h8V19z
+M19,12c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S19.55,12,19,12z M17,3H7
+C6.45,3,6,3.45,6,4v3h12V4C18,3.45,17.55,3,17,3z"/>
+</vector>
diff --git a/packages/SettingsLib/res/layout/preference_two_target.xml b/packages/SettingsLib/res/layout/preference_two_target.xml
index c2167f3e608d..4658924477a0 100644
--- a/packages/SettingsLib/res/layout/preference_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_two_target.xml
@@ -33,19 +33,17 @@
android:background="?android:attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:clipToPadding="false"
- android:layout_marginStart="4dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<LinearLayout
android:id="@+id/icon_frame"
- style="@style/preference_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
+ android:minWidth="56dp"
android:orientation="horizontal"
android:clipToPadding="false"
- android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<android.support.v7.internal.widget.PreferenceImageView
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 9318a7f62769..f45532432214 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -210,7 +210,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"აირჩიეთ Bluetooth აუდიოს LDAC კოდეკის\nდაკვრის ხარისხი"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"სტრიმინგი: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="dns_tls" msgid="6773814174391131955">"DNS-ის TLS-ის მეშვეობით გადაცემა"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"ჩართვის შემთხვევაში, ცადეთ DNS-ის TLS-ის მეშვეობით გადაცემა პორტზე 853."</string>
+ <string name="dns_tls_summary" msgid="3692494150251071380">"ჩართვის შემთხვევაში, DNS-ის TLS-ის მეშვეობით გადაცემის ცდა, პორტზე 853."</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"უსადენო ეკრანის სერტიფიცირების ვარიანტების ჩვენება"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-ს აღრიცხვის დონის გაზრდა, Wi‑Fi ამომრჩეველში ყოველ SSID RSSI-ზე ჩვენება"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ჩართვის შემთხვევაში, Wi‑Fi უფრო აქტიურად შეეცდება მობილურ ინტერნეტზე გადართვას, როცა Wi‑Fi სიგნალი სუსტია"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 725665dcc09b..d88adeac21c3 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -295,7 +295,7 @@
<string name="app_process_limit_title" msgid="4280600650253107163">"Лимит фоновых процессов"</string>
<string name="show_all_anrs" msgid="28462979638729082">"Все ANR"</string>
<string name="show_all_anrs_summary" msgid="641908614413544127">"Уведомлять о том, что приложение не отвечает"</string>
- <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Показывать предупреждения канала передачи оповещения"</string>
+ <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Показывать предупреждения канала передачи уведомлений"</string>
<string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Показывать предупреждение о новых уведомлениях приложения вне допустимого канала"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"Разрешить сохранение на внешние накопители"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Разрешить сохранение приложений на внешних накопителях (независимо от значений в манифесте)"</string>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index e2615709cfb7..bd963e96dcab 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -32,6 +32,7 @@
<dimen name="user_spinner_padding_sides">20dp</dimen>
<dimen name="user_spinner_item_height">56dp</dimen>
+ <dimen name="two_target_pref_small_icon_size">24dp</dimen>
<!-- Lock icon for preferences locked by admin -->
<dimen name="restricted_icon_size">16dp</dimen>
<dimen name="restricted_icon_padding">4dp</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a404759d0639..360848f72ddd 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -235,6 +235,27 @@
<!-- Message for the error dialog when BT pairing fails because the other device rejected the pairing. -->
<string name="bluetooth_pairing_rejected_error_message">Pairing rejected by <xliff:g id="device_name">%1$s</xliff:g>.</string>
+ <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=4875089335641234463] -->
+ <string name="bluetooth_talkback_computer">Computer</string>
+
+ <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=5140152177885220949] -->
+ <string name="bluetooth_talkback_headset">Headset</string>
+
+ <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=4260255181240622896] -->
+ <string name="bluetooth_talkback_phone">Phone</string>
+
+ <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=551146170554589119] -->
+ <string name="bluetooth_talkback_imaging">Imaging</string>
+
+ <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=26580326066627664] -->
+ <string name="bluetooth_talkback_headphone">Headphone</string>
+
+ <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=5165842622743212268] -->
+ <string name="bluetooth_talkback_input_peripheral">Input Peripheral</string>
+
+ <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=5615463912185280812] -->
+ <string name="bluetooth_talkback_bluetooth">Bluetooth</string>
+
<!-- Content description of the WIFI signal when WIFI is disabled for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_off">Wifi off.</string>
<!-- Content description of the WIFI signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml
index b7ea1d498ce1..3f312f4efda3 100644
--- a/packages/SettingsLib/res/values/styles.xml
+++ b/packages/SettingsLib/res/values/styles.xml
@@ -21,9 +21,4 @@
<style name="TextAppearanceMedium">
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
</style>
-
- <style name="preference_icon_frame">
- <item name="android:layout_marginStart">-4dp</item>
- <item name="android:minWidth">60dp</item>
- </style>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index 32e6389dc34f..c3a36e97be3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -28,6 +28,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
@@ -343,7 +344,8 @@ public class RestrictedLockUtils {
}
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- if (dpm == null) {
+ PackageManager pm = context.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) || dpm == null) {
return null;
}
boolean isAccountTypeDisabled = false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java b/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
index 1c161dffced9..8b39f60aa9ca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
@@ -21,41 +21,56 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
public class TwoTargetPreference extends Preference {
+ private boolean mUseSmallIcon;
+ private int mSmallIconSize;
+
public TwoTargetPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init();
+ init(context);
}
public TwoTargetPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init();
+ init(context);
}
public TwoTargetPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
+ init(context);
}
public TwoTargetPreference(Context context) {
super(context);
- init();
+ init(context);
}
- private void init() {
+ private void init(Context context) {
setLayoutResource(R.layout.preference_two_target);
+ mSmallIconSize = context.getResources().getDimensionPixelSize(
+ R.dimen.two_target_pref_small_icon_size);
final int secondTargetResId = getSecondTargetResId();
if (secondTargetResId != 0) {
setWidgetLayoutResource(secondTargetResId);
}
}
+ public void setUseSmallIcon(boolean useSmallIcon) {
+ mUseSmallIcon = useSmallIcon;
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
+ if (mUseSmallIcon) {
+ ImageView icon = holder.itemView.findViewById(android.R.id.icon);
+ icon.setLayoutParams(new LinearLayout.LayoutParams(mSmallIconSize, mSmallIconSize));
+ }
final View divider = holder.findViewById(R.id.two_target_divider);
final View widgetFrame = holder.findViewById(android.R.id.widget_frame);
final boolean shouldHideSecondTarget = shouldHideSecondTarget();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 7bda2314f4c4..3299cb2d1221 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -32,7 +32,7 @@ import java.util.List;
/**
* PanProfile handles Bluetooth PAN profile (NAP and PANU).
*/
-public final class PanProfile implements LocalBluetoothProfile {
+public class PanProfile implements LocalBluetoothProfile {
private static final String TAG = "PanProfile";
private static boolean V = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
index c9194268543a..0ee1dad9d744 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
@@ -1,9 +1,17 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.util.Pair;
import com.android.settingslib.R;
+import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
+
+import java.util.List;
public class Utils {
public static final boolean V = false; // verbose logging
@@ -40,4 +48,78 @@ public class Utils {
void onShowError(Context context, String name, int messageResId);
}
+ public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice) {
+ return getBtClassDrawableWithDescription(context, cachedDevice, 1 /* iconScale */);
+ }
+
+ public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice, float iconScale) {
+ BluetoothClass btClass = cachedDevice.getBtClass();
+ final int level = cachedDevice.getBatteryLevel();
+ if (btClass != null) {
+ switch (btClass.getMajorDeviceClass()) {
+ case BluetoothClass.Device.Major.COMPUTER:
+ return new Pair<>(getBluetoothDrawable(context, R.drawable.ic_bt_laptop, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_computer));
+
+ case BluetoothClass.Device.Major.PHONE:
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_bt_cellphone, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_phone));
+
+ case BluetoothClass.Device.Major.PERIPHERAL:
+ return new Pair<>(
+ getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass),
+ level, iconScale),
+ context.getString(R.string.bluetooth_talkback_input_peripheral));
+
+ case BluetoothClass.Device.Major.IMAGING:
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_settings_print, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_imaging));
+
+ default:
+ // unrecognized device class; continue
+ }
+ }
+
+ List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
+ for (LocalBluetoothProfile profile : profiles) {
+ int resId = profile.getDrawableResource(btClass);
+ if (resId != 0) {
+ return new Pair<>(getBluetoothDrawable(context, resId, level, iconScale), null);
+ }
+ }
+ if (btClass != null) {
+ if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_bt_headset_hfp, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_headset));
+ }
+ if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_bt_headphones_a2dp, level,
+ iconScale),
+ context.getString(R.string.bluetooth_talkback_headphone));
+ }
+ }
+ return new Pair<>(
+ getBluetoothDrawable(context, R.drawable.ic_settings_bluetooth, level, iconScale),
+ context.getString(R.string.bluetooth_talkback_bluetooth));
+ }
+
+ public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId,
+ int batteryLevel, float iconScale) {
+ if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ return BluetoothDeviceLayerDrawable.createLayerDrawable(context, resId, batteryLevel,
+ iconScale);
+ } else {
+ return context.getDrawable(resId);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 1771208398fd..1cb255bf8973 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -43,6 +43,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
+import libcore.util.TimeZoneFinder;
/**
* ZoneGetter is the utility class to get time zone and zone list, and both of them have display
@@ -376,10 +377,9 @@ public class ZoneGetter {
}
// Create a lookup of local zone IDs.
- localZoneIds = new HashSet<String>();
- for (String olsonId : libcore.icu.TimeZoneNames.forLocale(locale)) {
- localZoneIds.add(olsonId);
- }
+ List<String> zoneIds =
+ TimeZoneFinder.getInstance().lookupTimeZoneIdsByCountry(locale.getCountry());
+ localZoneIds = new HashSet<>(zoneIds);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 6aae226bb9a3..3c02f6a234f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -16,11 +16,13 @@
package com.android.settingslib.development;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.UserManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.SwitchPreference;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.preference.Preference;
@@ -95,6 +97,10 @@ public abstract class AbstractEnableAdbPreferenceController extends
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
+ if (isUserAMonkey()) {
+ return false;
+ }
+
if (TextUtils.equals(KEY_ENABLE_ADB, preference.getKey())) {
if (!isAdbEnabled()) {
showConfirmationDialog(preference);
@@ -117,4 +123,9 @@ public abstract class AbstractEnableAdbPreferenceController extends
LocalBroadcastManager.getInstance(mContext)
.sendBroadcast(new Intent(ACTION_ENABLE_ADB_STATE_CHANGED));
}
+
+ @VisibleForTesting
+ boolean isUserAMonkey() {
+ return ActivityManager.isUserAMonkey();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index ca366ea4b6cc..64de63520f87 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.UserManager;
@@ -56,6 +57,8 @@ public class RestrictedLockUtilsTest {
private DevicePolicyManager mDevicePolicyManager;
@Mock
private UserManager mUserManager;
+ @Mock
+ private PackageManager mPackageManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private RestrictedLockUtils.Proxy mProxy;
@@ -72,11 +75,32 @@ public class RestrictedLockUtilsTest {
.thenReturn(mDevicePolicyManager);
when(mContext.getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager);
+ when(mContext.getPackageManager())
+ .thenReturn(mPackageManager);
RestrictedLockUtils.sProxy = mProxy;
}
@Test
+ public void checkIfDevicePolicyServiceDisabled_noEnforceAdminForManagedProfile() {
+ when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(null);
+ final EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfAccountManagementDisabled(
+ mContext, "account_type", mUserId);
+
+ assertThat(enforcedAdmin).isEqualTo(null);
+ }
+
+ @Test
+ public void checkIfDeviceAdminFeatureDisabled_noEnforceAdminForManagedProfile() {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN))
+ .thenReturn(false);
+ final EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfAccountManagementDisabled(
+ mContext, "account_type", mUserId);
+
+ assertThat(enforcedAdmin).isEqualTo(null);
+ }
+
+ @Test
public void checkIfKeyguardFeaturesDisabled_noEnforcedAdminForManagedProfile() {
setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2});
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
index fa654cb18d83..b5ee5adc8135 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
@@ -16,34 +16,31 @@
package com.android.settingslib;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
import android.content.Context;
import android.support.v7.preference.PreferenceViewHolder;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
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;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class TwoTargetPreferenceTest {
private PreferenceViewHolder mViewHolder;
- @Mock
private View mDivider;
- @Mock
private View mWidgetFrame;
+ private View mRootView;
private TwoTargetPreference mPreference;
private Context mContext;
@@ -52,11 +49,11 @@ public class TwoTargetPreferenceTest {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = spy(new TwoTargetPreference(mContext));
- mViewHolder = PreferenceViewHolder.createInstanceForTests(mock(View.class));
- when(mViewHolder.findViewById(R.id.two_target_divider))
- .thenReturn(mDivider);
- when(mViewHolder.findViewById(android.R.id.widget_frame))
- .thenReturn(mWidgetFrame);
+ mRootView = View.inflate(mContext, R.layout.preference_two_target, null /* parent */);
+ mViewHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+
+ mDivider = mViewHolder.findViewById(R.id.two_target_divider);
+ mWidgetFrame = mViewHolder.findViewById(android.R.id.widget_frame);
}
@Test
@@ -65,8 +62,8 @@ public class TwoTargetPreferenceTest {
mPreference.onBindViewHolder(mViewHolder);
- verify(mDivider).setVisibility(View.GONE);
- verify(mWidgetFrame).setVisibility(View.GONE);
+ assertThat(mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mWidgetFrame.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -75,7 +72,37 @@ public class TwoTargetPreferenceTest {
mPreference.onBindViewHolder(mViewHolder);
- verify(mDivider).setVisibility(View.VISIBLE);
- verify(mWidgetFrame).setVisibility(View.VISIBLE);
+ assertThat(mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWidgetFrame.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void bind_smallIcon_shouldUseSmallIcon() {
+ mPreference.setUseSmallIcon(true);
+
+ mPreference.onBindViewHolder(mViewHolder);
+
+ final int smallIconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.two_target_pref_small_icon_size);
+ final LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mViewHolder
+ .findViewById(android.R.id.icon)
+ .getLayoutParams();
+
+ assertThat(layoutParams.width).isEqualTo(smallIconSize);
+ assertThat(layoutParams.height).isEqualTo(smallIconSize);
+ }
+
+ @Test
+ public void bind_normalIcon_shouldUseNormalIcon() {
+ mPreference.setUseSmallIcon(false);
+
+ mPreference.onBindViewHolder(mViewHolder);
+
+ final LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mViewHolder
+ .findViewById(android.R.id.icon)
+ .getLayoutParams();
+
+ assertThat(layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT);
+ assertThat(layoutParams.height).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index b1dbb0af54a7..4091ce1f173e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -59,7 +59,7 @@ public class CachedBluetoothDeviceTest {
@Mock
private A2dpProfile mA2dpProfile;
@Mock
- private HidProfile mHidProfile;
+ private PanProfile mPanProfile;
@Mock
private BluetoothDevice mDevice;
private CachedBluetoothDevice mCachedDevice;
@@ -74,7 +74,7 @@ public class CachedBluetoothDeviceTest {
when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
- when(mHidProfile.isProfileReady()).thenReturn(true);
+ when(mPanProfile.isProfileReady()).thenReturn(true);
mCachedDevice = spy(
new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice));
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -92,37 +92,37 @@ public class CachedBluetoothDeviceTest {
@Test
public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
// Test without battery level
- // Set HID profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected));
- // Set HID profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with battery level
mBatteryLevel = 10;
- // Set HID profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected_battery_level,
com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
- // Set HID profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
- // Set HID profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected));
- // Set HID profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@@ -130,10 +130,10 @@ public class CachedBluetoothDeviceTest {
public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() {
mBatteryLevel = 10;
- // Set HFP, A2DP and HID profile to be connected and test connection state summary
+ // Set HFP, A2DP and PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected_battery_level,
com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
@@ -158,7 +158,7 @@ public class CachedBluetoothDeviceTest {
com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
// Disconnect all profiles and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/UtilsTest.java
new file mode 100644
index 000000000000..5eb543ba3bae
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/UtilsTest.java
@@ -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.
+ */
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothDevice;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
+import com.android.settingslib.testutils.shadow.SettingsLibShadowResources;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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 UtilsTest {
+
+ @Test
+ public void testGetBluetoothDrawable_noBatteryLevel_returnSimpleDrawable() {
+ final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application,
+ R.drawable.ic_bt_laptop, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, 1 /* iconScale */);
+
+ assertThat(drawable).isNotInstanceOf(BluetoothDeviceLayerDrawable.class);
+ }
+
+ @Test
+ public void testGetBluetoothDrawable_hasBatteryLevel_returnLayerDrawable() {
+ final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application,
+ R.drawable.ic_bt_laptop, 10 /* batteryLevel */, 1 /* iconScale */);
+
+ assertThat(drawable).isInstanceOf(BluetoothDeviceLayerDrawable.class);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index bcabff30f345..32fa01c52f21 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -119,6 +120,18 @@ public class EnableAdbPreferenceControllerTest {
}
@Test
+ public void handlePreferenceTreeClick_isMonkeyUser_shouldBeFalse() {
+ mController = spy(mController);
+ doReturn(true).when(mController).isUserAMonkey();
+ when(mUserManager.isAdminUser()).thenReturn(true);
+ mController.displayPreference(mScreen);
+
+ final boolean handled = mController.handlePreferenceTreeClick(mPreference);
+
+ assertThat(handled).isFalse();
+ }
+
+ @Test
public void updateState_settingsOn_shouldCheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -161,6 +174,7 @@ public class EnableAdbPreferenceControllerTest {
}
@Override
- public void dismissConfirmationDialog() {}
+ public void dismissConfirmationDialog() {
+ }
}
}
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index d311ba87d5fb..527122873f23 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Upišite lozinku za otključavanje"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Upišite PIN za otključavanje"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Punjenje"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Brzo punjenje"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index c706900e21d3..9b48a4b1b564 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"잠금 해제하려면 비밀번호 입력"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"잠금 해제하려면 PIN 입력"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"잘못된 PIN 코드입니다."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"유효하지 않은 카드"</string>
<string name="keyguard_charged" msgid="2222329688813033109">"충전됨"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"충전 중"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"고속 충전 중"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 2c33c98b0dba..59f441290208 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Wpisz hasło, aby odblokować"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Wpisz kod PIN, aby odblokować"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nieprawidłowy kod PIN."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nieprawidłowa karta."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Naładowana"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Ładowanie"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Szybkie ładowanie"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 4855161d45d3..0ba941cd59d4 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"திறக்க, கடவுச்சொல்லை உள்ளிடவும்"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"திறக்க, பின்னை உள்ளிடவும்"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"தவறான பின் குறியீடு."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"செல்லாத சிம் கார்டு."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"சார்ஜ் செய்யப்பட்டது"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"சார்ஜ் ஆகிறது"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"வேகமாகச் சார்ஜாகிறது"</string>
diff --git a/packages/SystemUI/res/layout/operator_name.xml b/packages/SystemUI/res/layout/operator_name.xml
new file mode 100644
index 000000000000..c4f75e927604
--- /dev/null
+++ b/packages/SystemUI/res/layout/operator_name.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/operator_name_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ >
+ <com.android.systemui.statusbar.OperatorNameView
+ android:id="@+id/operator_name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:maxLength="20"
+ android:gravity="center_vertical|start"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true" />
+</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 35a947777dd1..b138df086253 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -63,7 +63,8 @@
android:layout_width="18dp"
android:layout_height="match_parent"
android:src="@drawable/qs_dual_tile_caret"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?android:attr/textColorPrimary"
+ android:visibility="gone" />
</LinearLayout>
<TextView
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index c6452c03459a..6de27acd741b 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -48,6 +48,11 @@
android:paddingEnd="8dp"
android:orientation="horizontal"
>
+ <ViewStub
+ android:id="@+id/operator_name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout="@layout/operator_name" />
<!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f9a423527db5..5d621cb79fc4 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -701,7 +701,7 @@
<string name="accessibility_action_divider_top_70" msgid="5090779195650364522">"Gore 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"Gore 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"Gore 30%"</string>
- <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"Dole cijeli ekran"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"Donji ekran kao cijeli ekran"</string>
<string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvaput dodirnite za uređivanje."</string>
<string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g> Dvaput dodirnite za dodavanje."</string>
<string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>. Dvaput dodirnite za odabir."</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 296842f86fa3..27d5f1b89868 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -233,8 +233,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mode kerja aktif."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Mode kerja dinonaktifkan."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Mode kerja diaktifkan."</string>
- <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Penghemat Data nonaktif."</string>
- <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Penghemat Data diaktifkan."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Penghemat Kuota Internet nonaktif."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Penghemat Kuota Internet diaktifkan."</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"Kecerahan tampilan"</string>
<string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Mengisi daya"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G-3G dijeda"</string>
@@ -636,9 +636,9 @@
<string name="headset" msgid="4534219457597457353">"Headset"</string>
<string name="accessibility_status_bar_headphones" msgid="9156307120060559989">"Headphone terhubung"</string>
<string name="accessibility_status_bar_headset" msgid="8666419213072449202">"Headset terhubung"</string>
- <string name="data_saver" msgid="5037565123367048522">"Penghemat Data"</string>
- <string name="accessibility_data_saver_on" msgid="8454111686783887148">"Penghemat Data aktif"</string>
- <string name="accessibility_data_saver_off" msgid="8841582529453005337">"Penghemat Data nonaktif"</string>
+ <string name="data_saver" msgid="5037565123367048522">"Penghemat Kuota Internet"</string>
+ <string name="accessibility_data_saver_on" msgid="8454111686783887148">"Penghemat Kuota Internet aktif"</string>
+ <string name="accessibility_data_saver_off" msgid="8841582529453005337">"Penghemat Kuota Internet nonaktif"</string>
<string name="switch_bar_on" msgid="1142437840752794229">"Aktif"</string>
<string name="switch_bar_off" msgid="8803270596930432874">"Nonaktif"</string>
<string name="nav_bar" msgid="1993221402773877607">"Bilah navigasi"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0fe81d9dfef1..f54115b0c9e2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -301,6 +301,9 @@
<!-- Enable the default volume dialog -->
<bool name="enable_volume_ui">true</bool>
+ <!-- Whether to show operator name in the status bar -->
+ <bool name="config_showOperatorNameInStatusBar">false</bool>
+
<!-- Duration of the full carrier network change icon animation. -->
<integer name="carrier_network_change_anim_time">3000</integer>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
new file mode 100644
index 000000000000..a46fab35f66b
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.recents.view;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
+
+import java.util.List;
+import java.util.concurrent.FutureTask;
+
+/**
+ * To be implemented by a particular animation to asynchronously provide the animation specs for a
+ * particular transition.
+ */
+public abstract class AppTransitionAnimationSpecsFuture {
+
+ private final Handler mHandler;
+ private FutureTask<List<AppTransitionAnimationSpec>> mComposeTask = new FutureTask<>(() -> {
+ synchronized (AppTransitionAnimationSpecsFuture.this) {
+ return composeSpecs();
+ }
+ });
+
+ private final IAppTransitionAnimationSpecsFuture mFuture =
+ new IAppTransitionAnimationSpecsFuture.Stub() {
+ @Override
+ public AppTransitionAnimationSpec[] get() throws RemoteException {
+ try {
+ if (!mComposeTask.isDone()) {
+ mHandler.post(mComposeTask);
+ }
+ List<AppTransitionAnimationSpec> specs = mComposeTask.get();
+ if (specs == null) {
+ return null;
+ }
+
+ AppTransitionAnimationSpec[] arr = new AppTransitionAnimationSpec[specs.size()];
+ specs.toArray(arr);
+ return arr;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ };
+
+ public AppTransitionAnimationSpecsFuture(Handler handler) {
+ mHandler = handler;
+ }
+
+ /**
+ * Returns the future to handle the call from window manager.
+ */
+ public final IAppTransitionAnimationSpecsFuture getFuture() {
+ return mFuture;
+ }
+
+ /**
+ * Called ahead of the future callback to compose the specs to be returned in the future.
+ */
+ public final void composeSpecsSynchronous() {
+ if (Looper.myLooper() != mHandler.getLooper()) {
+ throw new RuntimeException("composeSpecsSynchronous() called from wrong looper");
+ }
+ mComposeTask.run();
+ }
+
+ public abstract List<AppTransitionAnimationSpec> composeSpecs();
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
new file mode 100644
index 000000000000..3095d151726d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.recents.view;
+
+import android.app.ActivityOptions;
+import android.app.ActivityOptions.OnAnimationStartedListener;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+import android.view.View;
+
+import java.util.function.Consumer;
+
+/**
+ * A helper class to create transitions to/from an App to Recents.
+ */
+public class RecentsTransition {
+
+ /**
+ * Creates a new transition aspect scaled transition activity options.
+ */
+ public static ActivityOptions createAspectScaleAnimation(Context context, Handler handler,
+ boolean scaleUp, AppTransitionAnimationSpecsFuture animationSpecsFuture,
+ OnAnimationStartedListener animationStartCallback) {
+ final OnAnimationStartedListener animStartedListener = new OnAnimationStartedListener() {
+ private boolean mHandled;
+
+ @Override
+ public void onAnimationStarted() {
+ // OnAnimationStartedListener can be called numerous times, so debounce here to
+ // prevent multiple callbacks
+ if (mHandled) {
+ return;
+ }
+ mHandled = true;
+
+ if (animationStartCallback != null) {
+ animationStartCallback.onAnimationStarted();
+ }
+ }
+ };
+ final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(
+ context, handler,
+ animationSpecsFuture != null ? animationSpecsFuture.getFuture() : null,
+ animStartedListener, scaleUp);
+ return opts;
+ }
+
+ /**
+ * Wraps a animation-start callback in a binder that can be called from window manager.
+ */
+ public static IRemoteCallback wrapStartedListener(Handler handler,
+ OnAnimationStartedListener listener) {
+ if (listener == null) {
+ return null;
+ }
+ return new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ handler.post(listener::onAnimationStarted);
+ }
+ };
+ }
+
+ /**
+ * @return a {@link GraphicBuffer} with the {@param view} drawn into it. Result can be null if
+ * we were unable to allocate a hardware bitmap.
+ */
+ public static GraphicBuffer drawViewIntoGraphicBuffer(int width, int height, View view,
+ float scale, int eraseColor) {
+ final Bitmap hwBitmap = createHardwareBitmap(width, height, (c) -> {
+ c.scale(scale, scale);
+ if (eraseColor != 0) {
+ c.drawColor(eraseColor);
+ }
+ if (view != null) {
+ view.draw(c);
+ }
+ });
+ return hwBitmap != null ? hwBitmap.createGraphicBufferHandle() : null;
+ }
+
+ /**
+ * @return a hardware {@link Bitmap} after being drawn with the {@param consumer}. Result can be
+ * null if we were unable to allocate a hardware bitmap.
+ */
+ public static Bitmap createHardwareBitmap(int width, int height, Consumer<Canvas> consumer) {
+ RenderNode node = RenderNode.create("RecentsTransition", null);
+ node.setLeftTopRightBottom(0, 0, width, height);
+ node.setClipToBounds(false);
+ DisplayListCanvas c = node.start(width, height);
+ consumer.accept(c);
+ node.end(c);
+ return ThreadedRenderer.createHardwareBitmap(node, width, height);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 3f93f76af7e4..090617dc89c5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -50,11 +50,13 @@ public class ActivityManagerWrapper {
private final PackageManager mPackageManager;
private final IconDrawableFactory mDrawableFactory;
+ private final BackgroundExecutor mBackgroundExecutor;
private ActivityManagerWrapper() {
final Context context = AppGlobals.getInitialApplication();
mPackageManager = context.getPackageManager();
mDrawableFactory = IconDrawableFactory.newInstance(context);
+ mBackgroundExecutor = BackgroundExecutor.get();
}
public static ActivityManagerWrapper getInstance() {
@@ -198,4 +200,52 @@ public class ActivityManagerWrapper {
}
return label;
}
+
+ /**
+ * Requests that the system close any open system windows (including other SystemUI).
+ */
+ public void closeSystemWindows(String reason) {
+ mBackgroundExecutor.submit(() -> {
+ try {
+ ActivityManager.getService().closeSystemDialogs(reason);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to close system windows", e);
+ }
+ });
+ }
+
+ /**
+ * Removes a task by id.
+ */
+ public void removeTask(int taskId) {
+ mBackgroundExecutor.submit(() -> {
+ try {
+ ActivityManager.getService().removeTask(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove task=" + taskId, e);
+ }
+ });
+ }
+
+ /**
+ * Cancels the current window transtion to/from Recents for the given task id.
+ */
+ public void cancelWindowTransition(int taskId) {
+ try {
+ ActivityManager.getService().cancelTaskWindowTransition(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
+ }
+ }
+
+ /**
+ * Cancels the current thumbnail transtion to/from Recents for the given task id.
+ */
+ public void cancelThumbnailTransition(int taskId) {
+ try {
+ ActivityManager.getService().cancelTaskThumbnailTransition(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
+ }
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java
new file mode 100644
index 000000000000..26e1813792bd
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * Offloads work from other threads by running it in a background thread.
+ */
+public class BackgroundExecutor {
+
+ private static final BackgroundExecutor sInstance = new BackgroundExecutor();
+
+ private final ExecutorService mExecutorService = Executors.newFixedThreadPool(2);
+
+ /**
+ * @return the static instance of the background executor.
+ */
+ public static BackgroundExecutor get() {
+ return sInstance;
+ }
+
+ /**
+ * Runs the given {@param runnable} on one of the background executor threads.
+ */
+ public Future<?> submit(Runnable runnable) {
+ return mExecutorService.submit(runnable);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 6d0952abe2f8..36310f11c22d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -14,24 +14,22 @@
* limitations under the License.
*/
-package com.android.systemui.recents.misc;
+package com.android.systemui.shared.system;
import android.app.ActivityManager.TaskSnapshot;
-import android.content.Context;
import android.os.UserHandle;
import android.util.Log;
/**
- * An abstract class to track task stack changes.
- * Classes should implement this instead of {@link android.app.ITaskStackListener}
- * to reduce IPC calls from system services. These callbacks will be called on the main thread.
+ * An interface to track task stack changes. Classes should implement this instead of
+ * {@link android.app.ITaskStackListener} to reduce IPC calls from system services.
*/
public abstract class TaskStackChangeListener {
- /**
- * NOTE: This call is made of the thread that the binder call comes in on.
- */
+ // Binder thread callbacks
public void onTaskStackChangedBackground() { }
+
+ // Main thread callbacks
public void onTaskStackChanged() { }
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
@@ -45,17 +43,16 @@ public abstract class TaskStackChangeListener {
public void onTaskProfileLocked(int taskId, int userId) { }
/**
- * Checks that the current user matches the user's SystemUI process. Since
+ * Checks that the current user matches the process. Since
* {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
- * TaskStackChangeListener should make this call to verify that we don't act on events from other
- * user's processes.
+ * {@link TaskStackChangeListener} should make this call to verify that we don't act on events
+ * originating from another user's interactions.
*/
- protected final boolean checkCurrentUserId(Context context, boolean debug) {
+ protected final boolean checkCurrentUserId(int currentUserId, boolean debug) {
int processUserId = UserHandle.myUserId();
- int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser();
if (processUserId != currentUserId) {
if (debug) {
- Log.d(SystemServicesProxy.TAG, "UID mismatch. SystemUI is running uid=" + processUserId
+ Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId
+ " and the current user is uid=" + currentUserId);
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 8eb70f04cf32..95fc96f5f228 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents.misc;
+package com.android.systemui.shared.system;
import android.app.ActivityManager.TaskSnapshot;
import android.app.IActivityManager;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2bb992c449b6..623fe53f5d7f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -76,8 +76,8 @@ import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
import com.google.android.collect.Lists;
@@ -1738,6 +1738,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
mFailedAttempts.delete(sCurrentUser);
}
+ public ServiceState getServiceState(int subId) {
+ return mServiceStates.get(subId);
+ }
+
public int getFailedUnlockAttempts(int userId) {
return mFailedAttempts.get(userId, 0);
}
@@ -1772,7 +1776,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
}
- private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ private final SysUiTaskStackChangeListener
+ mTaskStackListener = new SysUiTaskStackChangeListener() {
@Override
public void onTaskStackChangedBackground() {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/DemoMode.java b/packages/SystemUI/src/com/android/systemui/DemoMode.java
index 11996d078bc3..5c3971571b87 100644
--- a/packages/SystemUI/src/com/android/systemui/DemoMode.java
+++ b/packages/SystemUI/src/com/android/systemui/DemoMode.java
@@ -37,4 +37,5 @@ public interface DemoMode {
public static final String COMMAND_STATUS = "status";
public static final String COMMAND_NOTIFICATIONS = "notifications";
public static final String COMMAND_VOLUME = "volume";
+ public static final String COMMAND_OPERATOR = "operator";
}
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 44a044bc4ce4..880ae709b59d 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -28,7 +28,7 @@ public interface RecentsComponent {
/**
* Docks the top-most task and opens recents.
*/
- boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
+ boolean splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds,
int metricsDockAction);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 4c3d5badf934..4119ec076dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -26,14 +26,16 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
public class WorkLockActivityController {
+ private static final String TAG = WorkLockActivityController.class.getSimpleName();
+
private final Context mContext;
- private final SystemServicesProxy mSsp;
private final IActivityManager mIam;
public WorkLockActivityController(Context context) {
@@ -43,17 +45,22 @@ public class WorkLockActivityController {
@VisibleForTesting
WorkLockActivityController(Context context, SystemServicesProxy ssp, IActivityManager am) {
mContext = context;
- mSsp = ssp;
mIam = am;
- mSsp.registerTaskStackListener(mLockListener);
+ ssp.registerTaskStackListener(mLockListener);
}
private void startWorkChallengeInTask(int taskId, int userId) {
+ ActivityManager.TaskDescription taskDescription = null;
+ try {
+ taskDescription = mIam.getTaskDescription(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get description for task=" + taskId);
+ }
Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
.setComponent(new ComponentName(mContext, WorkLockActivity.class))
.putExtra(Intent.EXTRA_USER_ID, userId)
- .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, mSsp.getTaskDescription(taskId))
+ .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, taskDescription)
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -67,7 +74,11 @@ public class WorkLockActivityController {
} else {
// Starting the activity inside the task failed. We can't be sure why, so to be
// safe just remove the whole task if it still exists.
- mSsp.removeTask(taskId);
+ try {
+ mIam.removeTask(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get description for task=" + taskId);
+ }
}
}
@@ -96,7 +107,7 @@ public class WorkLockActivityController {
}
}
- private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() {
+ private final SysUiTaskStackChangeListener mLockListener = new SysUiTaskStackChangeListener() {
@Override
public void onTaskProfileLocked(int taskId, int userId) {
startWorkChallengeInTask(taskId, userId);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
index e6d6c5586ad8..db4f988a9122 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
@@ -18,6 +18,8 @@ package com.android.systemui.pip.phone;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
@@ -77,7 +79,8 @@ public class InputConsumerController {
}
}
- private IWindowManager mWindowManager;
+ private final IWindowManager mWindowManager;
+ private final IBinder mToken;
private PipInputEventReceiver mInputEventReceiver;
private TouchListener mListener;
@@ -85,6 +88,7 @@ public class InputConsumerController {
public InputConsumerController(IWindowManager windowManager) {
mWindowManager = windowManager;
+ mToken = new Binder();
registerInputConsumer();
}
@@ -122,7 +126,7 @@ public class InputConsumerController {
final InputChannel inputChannel = new InputChannel();
try {
mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
+ mWindowManager.createInputConsumer(mToken, INPUT_CONSUMER_PIP, inputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create PIP input consumer", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 7e87666a18f5..7ef0f1539dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -40,8 +40,9 @@ import android.view.WindowManagerGlobal;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.component.ExpandPipEvent;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.io.PrintWriter;
@@ -69,7 +70,7 @@ public class PipManager implements BasePipManager {
/**
* Handler for system task stack changes.
*/
- TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() {
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
mTouchHandler.onActivityPinned();
@@ -202,9 +203,9 @@ public class PipManager implements BasePipManager {
StackInfo stackInfo = mActivityManager.getStackInfo(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null && stackInfo.taskIds != null) {
- SystemServicesProxy ssp = SystemServicesProxy.getInstance(mContext);
+ ActivityManagerWrapper am = ActivityManagerWrapper.getInstance();
for (int taskId : stackInfo.taskIds) {
- ssp.cancelThumbnailTransition(taskId);
+ am.cancelThumbnailTransition(taskId);
}
}
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index c92562b4436f..5a91defc42df 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -45,8 +45,8 @@ import android.view.WindowManagerGlobal;
import com.android.systemui.R;
import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -620,7 +620,7 @@ public class PipManager implements BasePipManager {
return false;
}
- private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ private SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 3199decb3ec1..532ead1f0475 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs;
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE;
import android.app.ActivityManager;
@@ -37,7 +39,6 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
@@ -51,10 +52,11 @@ import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.R.dimen;
import com.android.systemui.R.id;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.TouchAnimator.ListenerAdapter;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.ExpandableIndicator;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
@@ -70,7 +72,7 @@ import com.android.systemui.tuner.TunerService;
public class QSFooterImpl extends FrameLayout implements QSFooter,
NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
- SignalCallback {
+ SignalCallback, CommandQueue.Callbacks {
private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
private ActivityStarter mActivityStarter;
@@ -83,6 +85,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private View mAlarmStatusCollapsed;
private View mDate;
+ private boolean mQsDisabled;
private QSPanel mQsPanel;
private boolean mExpanded;
@@ -278,9 +281,16 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
@Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+ }
+
+ @Override
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
super.onDetachedFromWindow();
}
@@ -302,6 +312,14 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
return findViewById(R.id.expand_indicator);
}
+ @Override
+ public void disable(int state1, int state2, boolean animate) {
+ final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
+ if (disabled == mQsDisabled) return;
+ mQsDisabled = disabled;
+ updateEverything();
+ }
+
public void updateEverything() {
post(() -> {
updateVisibilities();
@@ -311,8 +329,13 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private void updateVisibilities() {
updateAlarmVisibilities();
+
+ mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
+
+ mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 00b883a541db..947b23f8f6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -25,7 +25,7 @@ import android.widget.Space;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.*;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.plugins.qs.QSTileView;
@@ -43,6 +43,7 @@ public class QuickQSPanel extends QSPanel {
public static final String NUM_QUICK_TILES = "sysui_qqs_count";
+ private boolean mDisabledByPolicy;
private int mMaxTiles;
protected QSPanel mFullPanel;
@@ -151,6 +152,30 @@ public class QuickQSPanel extends QSPanel {
return Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, 6);
}
+ void setDisabledByPolicy(boolean disabled) {
+ if (disabled != mDisabledByPolicy) {
+ mDisabledByPolicy = disabled;
+ setVisibility(disabled ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ /**
+ * Sets the visibility of this {@link QuickQSPanel}. This method has no effect when this panel
+ * is disabled by policy through {@link #setDisabledByPolicy(boolean)}, and in this case the
+ * visibility will always be {@link View#GONE}. This method is called externally by
+ * {@link QSAnimator} only.
+ */
+ @Override
+ public void setVisibility(int visibility) {
+ if (mDisabledByPolicy) {
+ if (getVisibility() == View.GONE) {
+ return;
+ }
+ visibility = View.GONE;
+ }
+ super.setVisibility(visibility);
+ }
+
private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 0709e229bd4d..398592ad1699 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -14,6 +14,9 @@
package com.android.systemui.qs;
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.app.StatusBarManager.DISABLE_NONE;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -30,13 +33,14 @@ import com.android.systemui.BatteryMeterView;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.R.id;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-
-public class QuickStatusBarHeader extends RelativeLayout {
+public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks {
private ActivityStarter mActivityStarter;
@@ -44,6 +48,7 @@ public class QuickStatusBarHeader extends RelativeLayout {
private boolean mExpanded;
private boolean mListening;
+ private boolean mQsDisabled;
protected QuickQSPanel mHeaderQsPanel;
protected QSTileHost mHost;
@@ -119,9 +124,25 @@ public class QuickStatusBarHeader extends RelativeLayout {
}
@Override
+ public void disable(int state1, int state2, boolean animate) {
+ final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
+ if (disabled == mQsDisabled) return;
+ mQsDisabled = disabled;
+ mHeaderQsPanel.setDisabledByPolicy(disabled);
+ final int rawHeight = (int) getResources().getDimension(R.dimen.status_bar_header_height);
+ getLayoutParams().height = disabled ? (rawHeight - mHeaderQsPanel.getHeight()) : rawHeight;
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+ }
+
+ @Override
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
+ SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
super.onDetachedFromWindow();
}
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 1aecdceb378c..0e4a9fe32911 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 BluetoothBatteryTileIcon(batteryLevel,
+ state.icon = new BluetoothBatteryTileIcon(lastDevice,
mContext.getResources().getFraction(
R.fraction.bt_battery_scale_fraction, 1, 1));
}
@@ -213,18 +213,19 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
}
private class BluetoothBatteryTileIcon extends Icon {
- private int mLevel;
private float mIconScale;
+ private CachedBluetoothDevice mDevice;
- BluetoothBatteryTileIcon(int level, float iconScale) {
- mLevel = level;
+ BluetoothBatteryTileIcon(CachedBluetoothDevice device, float iconScale) {
mIconScale = iconScale;
+ mDevice = device;
}
@Override
public Drawable getDrawable(Context context) {
- return createLayerDrawable(context,
- R.drawable.ic_qs_bluetooth_connected, mLevel, mIconScale);
+ // This method returns Pair<Drawable, String> while first value is the drawable
+ return com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription(
+ context, mDevice, mIconScale).first;
}
}
@@ -306,7 +307,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
item.iconResId = R.drawable.ic_qs_bluetooth_connected;
int batteryLevel = device.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- item.icon = new BluetoothBatteryTileIcon(batteryLevel,
+ item.icon = new BluetoothBatteryTileIcon(device,
1 /* iconScale */);
item.line2 = mContext.getString(
R.string.quick_settings_connected_battery_level,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
index 9214eef61df5..5ae7f22c4905 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -31,7 +31,7 @@ oneway interface IRecentsNonSystemUserCallbacks {
void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
void toggleRecents(int recentsGrowTarget);
void onConfigurationChanged();
- void dockTopTask(int topTaskId, int dragMode, int stackCreateMode,
+ void splitPrimaryTask(int topTaskId, int dragMode, int stackCreateMode,
in Rect initialBounds);
void onDraggingInRecents(float distanceFromTop);
void onDraggingInRecentsEnded(float velocity);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index ce1438a14e52..61c5167c8c02 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -63,6 +63,7 @@ import com.android.systemui.recents.events.component.ShowUserToastEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
@@ -246,7 +247,7 @@ public class Recents extends SystemUI
return;
}
- sSystemServicesProxy.sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
+ ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
@@ -394,7 +395,7 @@ public class Recents extends SystemUI
}
@Override
- public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
+ public boolean splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds,
int metricsDockAction) {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
@@ -426,15 +427,16 @@ public class Recents extends SystemUI
runningTask.topActivity.flattenToShortString());
}
if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds);
+ mImpl.splitPrimaryTask(runningTask.id, dragMode, stackCreateMode,
+ initialBounds);
} else {
if (mSystemToUserCallbacks != null) {
IRecentsNonSystemUserCallbacks callbacks =
mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
if (callbacks != null) {
try {
- callbacks.dockTopTask(runningTask.id, dragMode, stackCreateMode,
- initialBounds);
+ callbacks.splitPrimaryTask(runningTask.id, dragMode,
+ stackCreateMode, initialBounds);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b75a142864e8..c666d572e10a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -16,6 +16,9 @@
package com.android.systemui.recents;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
+
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.TaskStackBuilder;
@@ -92,6 +95,7 @@ import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.TaskStack;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
@@ -280,8 +284,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
new DismissRecentsToHomeAnimationStarted(animateTaskViews);
dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
overrideAnimation));
- Recents.getSystemServices().sendCloseSystemWindows(
- StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
+ ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
EventBus.getDefault().send(dismissEvent);
}
@@ -706,9 +709,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
int launchToTaskId = launchState.launchedToTaskId;
if (launchToTaskId != -1 &&
(event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.cancelWindowTransition(launchState.launchedToTaskId);
- ssp.cancelThumbnailTransition(getTaskId());
+ ActivityManagerWrapper am = ActivityManagerWrapper.getInstance();
+ am.cancelWindowTransition(launchState.launchedToTaskId);
+ am.cancelThumbnailTransition(getTaskId());
}
}
@@ -755,8 +758,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
loader.deleteTaskData(event.task, false);
// Remove the task from activity manager
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.removeTask(event.task.key.id);
+ ActivityManagerWrapper.getInstance().removeTask(event.task.key.id);
}
public final void onBusEvent(TaskViewDismissedEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 868ed64bab2e..96fae35f5d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.View.MeasureSpec;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
+
import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
@@ -70,26 +72,28 @@ import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskStack;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.recents.views.RecentsTransitionHelper;
-import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.DividerView;
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
+import java.util.List;
/**
* An implementation of the Recents component for the current user. For secondary users, this can
@@ -113,10 +117,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
/**
- * An implementation of TaskStackChangeListener, that allows us to listen for changes to the system
+ * An implementation of SysUiTaskStackChangeListener, that allows us to listen for changes to the system
* task stacks and update recents accordingly.
*/
- class TaskStackListenerImpl extends TaskStackChangeListener {
+ class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
@Override
public void onTaskStackChangedBackground() {
@@ -445,7 +449,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
growTarget);
// Only close the other system windows if we are actually showing recents
- ssp.sendCloseSystemWindows(StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
+ ActivityManagerWrapper.getInstance().closeSystemWindows(
+ SYSTEM_DIALOG_REASON_RECENT_APPS);
mLastToggleTime = SystemClock.elapsedRealtime();
}
} catch (ActivityNotFoundException e) {
@@ -641,13 +646,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
showRelativeAffiliatedTask(false);
}
- public void dockTopTask(int topTaskId, int dragMode,
- int stackCreateMode, Rect initialBounds) {
+ public void splitPrimaryTask(int taskId, int dragMode, int stackCreateMode,
+ Rect initialBounds) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Make sure we inform DividerView before we actually start the activity so we can change
// the resize mode already.
- if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {
+ if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
showRecents(
false /* triggeredFromAltTab */,
@@ -864,22 +869,22 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
windowOverrideRect);
RectF toTaskRect = toTransform.rect;
- AppTransitionAnimationSpecsFuture future =
- new RecentsTransitionHelper(mContext).getAppTransitionFuture(
- () -> {
- Rect rect = new Rect();
- toTaskRect.round(rect);
- GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask,
- toTransform);
- return Lists.newArrayList(new AppTransitionAnimationSpec(
- toTask.key.id, thumbnail, rect));
- });
+ AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(mHandler) {
+ @Override
+ public List<AppTransitionAnimationSpec> composeSpecs() {
+ Rect rect = new Rect();
+ toTaskRect.round(rect);
+ GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
+ return Lists.newArrayList(new AppTransitionAnimationSpec(toTask.key.id, thumbnail,
+ rect));
+ }
+ };
// For low end ram devices, wait for transition flag is reset when Recents entrance
// animation is complete instead of when the transition animation starts
- return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
- mHandler, future.getFuture(), isLowRamDevice ? null : mResetToggleFlagListener,
- false /* scaleUp */), future);
+ return new Pair<>(RecentsTransition.createAspectScaleAnimation(mContext, mHandler,
+ false /* scaleUp */, future, isLowRamDevice ? null : mResetToggleFlagListener),
+ future);
}
/**
@@ -919,7 +924,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
mHeaderBar.onTaskViewSizeChanged(width, height);
if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
+ return RecentsTransition.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
null, 1f, 0xFFff0000);
} else {
// Workaround for b/27815919, reset the callback so that we do not trigger an
@@ -932,7 +937,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
disabledInSafeMode);
mHeaderBar.onTaskDataLoaded();
mHeaderBar.setDimAlpha(toTransform.dimAlpha);
- return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
+ return RecentsTransition.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
mHeaderBar, 1f, 0);
}
}
@@ -1047,7 +1052,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
Recents.getSystemServices().startActivityAsUserAsync(intent, opts);
EventBus.getDefault().send(new RecentsActivityStartingEvent());
if (future != null) {
- future.precacheSpecs();
+ future.composeSpecsSynchronous();
}
});
EventBus.getDefault().send(hideMenuEvent);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
index ff9e89e9e00b..9493c78f6278 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
@@ -90,7 +90,7 @@ public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub {
}
@Override
- public void dockTopTask(int topTaskId, int dragMode, int stackCreateMode,
+ public void splitPrimaryTask(int topTaskId, int dragMode, int stackCreateMode,
Rect initialBounds) throws RemoteException {
SomeArgs args = SomeArgs.obtain();
args.argi1 = topTaskId;
@@ -144,7 +144,7 @@ public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub {
break;
case MSG_DOCK_TOP_TASK:
args = (SomeArgs) msg.obj;
- mImpl.dockTopTask(args.argi1, args.argi2, args.argi3 = 0,
+ mImpl.splitPrimaryTask(args.argi1, args.argi2, args.argi3 = 0,
(Rect) args.arg1);
break;
case MSG_ON_DRAGGING_IN_RECENTS:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java
new file mode 100644
index 000000000000..5d7f1ba5eaf4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.misc;
+
+import android.content.Context;
+
+import com.android.systemui.shared.system.TaskStackChangeListener;
+
+/**
+ * An implementation of {@link TaskStackChangeListener}.
+ */
+public abstract class SysUiTaskStackChangeListener extends TaskStackChangeListener {
+
+ /**
+ * Checks that the current user matches the user's SystemUI process.
+ */
+ protected final boolean checkCurrentUserId(Context context, boolean debug) {
+ int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser();
+ return checkCurrentUserId(currentUserId, debug);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 55ec5e7ed2ce..14b91f7aeaa6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -79,6 +79,7 @@ import com.android.systemui.UiOffloadThread;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImpl;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.policy.UserInfoController;
import java.util.List;
@@ -291,7 +292,7 @@ public class SystemServicesProxy {
try {
final ActivityOptions options = ActivityOptions.makeBasic();
- options.setDockCreateMode(createMode);
+ options.setSplitScreenCreateMode(createMode);
options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
mIam.startActivityFromRecents(taskId, options.toBundle());
return true;
@@ -301,14 +302,15 @@ public class SystemServicesProxy {
return false;
}
- /** Docks an already resumed task to the side of the screen. */
- public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
+ /** Moves an already resumed task to the side of the screen to initiate split screen. */
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
+ Rect initialBounds) {
if (mIam == null) {
return false;
}
try {
- return mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */,
+ return mIam.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
false /* animate */, initialBounds);
} catch (RemoteException e) {
e.printStackTrace();
@@ -363,32 +365,6 @@ public class SystemServicesProxy {
return insets.right > 0;
}
- /**
- * Cancels the current window transtion to/from Recents for the given task id.
- */
- public void cancelWindowTransition(int taskId) {
- if (mIam == null) return;
-
- try {
- mIam.cancelTaskWindowTransition(taskId);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Cancels the current thumbnail transtion to/from Recents for the given task id.
- */
- public void cancelThumbnailTransition(int taskId) {
- if (mIam == null) return;
-
- try {
- mIam.cancelTaskThumbnailTransition(taskId);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
/** Set the task's windowing mode. */
public void setTaskWindowingMode(int taskId, int windowingMode) {
if (mIam == null) return;
@@ -414,18 +390,6 @@ public class SystemServicesProxy {
});
}
- /**
- * Sends a message to close other system windows.
- */
- public void sendCloseSystemWindows(String reason) {
- mUiOffloadThread.submit(() -> {
- try {
- mIam.closeSystemDialogs(reason);
- } catch (RemoteException e) {
- }
- });
- }
-
public ActivityManager.TaskDescription getTaskDescription(int taskId) {
try {
return mIam.getTaskDescription(taskId);
@@ -622,7 +586,7 @@ public class SystemServicesProxy {
* Registers a task stack listener with the system.
* This should be called on the main thread.
*/
- public void registerTaskStackListener(TaskStackChangeListener listener) {
+ public void registerTaskStackListener(SysUiTaskStackChangeListener listener) {
if (mIam == null) return;
synchronized (mTaskStackChangeListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java b/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
index 59f28680a6e0..65b96fbb52f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java
@@ -16,8 +16,8 @@
package com.android.systemui.recents.views;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
@@ -71,19 +71,19 @@ public class DockState implements DropTarget {
public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
null, null, null);
public static final DockState LEFT = new DockState(DOCKED_LEFT,
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
new RectF(0, 0, 0.5f, 1));
public static final DockState TOP = new DockState(DOCKED_TOP,
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
new RectF(0, 0, 1, 0.5f));
public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
- DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
+ SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
new RectF(0.5f, 0, 1, 1));
public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
- DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
+ SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
new RectF(0, 0.5f, 1, 1));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
new file mode 100644
index 000000000000..99390ecfe08c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.content.Context;
+import android.graphics.GraphicBuffer;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.AppTransitionAnimationSpec;
+
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper class to create the transition app animation specs to/from Recents
+ */
+public class RecentsTransitionComposer {
+
+ private static final String TAG = "RecentsTransitionComposer";
+
+ private Context mContext;
+ private TaskViewTransform mTmpTransform = new TaskViewTransform();
+
+ public RecentsTransitionComposer(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Composes a single animation spec for the given {@link TaskView}
+ */
+ private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
+ TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
+ GraphicBuffer b = null;
+ if (addHeaderBitmap) {
+ b = composeHeaderBitmap(taskView, transform);
+ if (b == null) {
+ return null;
+ }
+ }
+
+ Rect taskRect = new Rect();
+ transform.rect.round(taskRect);
+ // Disable in for low ram devices because each task does in Recents does not have fullscreen
+ // height (stackView height) and when transitioning to fullscreen app, the code below would
+ // force the task thumbnail to full stackView height immediately causing the transition
+ // jarring.
+ if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() !=
+ stackView.getStack().getStackFrontMostTask()) {
+ taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
+ }
+ return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
+ }
+
+ /**
+ * Composes the transition spec when docking a task, which includes a full task bitmap.
+ */
+ public List<AppTransitionAnimationSpec> composeDockAnimationSpec(TaskView taskView,
+ Rect bounds) {
+ mTmpTransform.fillIn(taskView);
+ Task task = taskView.getTask();
+ GraphicBuffer buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform);
+ return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, buffer,
+ bounds));
+ }
+
+ /**
+ * Composes the animation specs for all the tasks in the target stack.
+ */
+ public List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
+ final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
+ // Calculate the offscreen task rect (for tasks that are not backed by views)
+ TaskView taskView = stackView.getChildViewForTask(task);
+ TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
+ Rect offscreenTaskRect = new Rect();
+ stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
+
+ // If this is a full screen stack, the transition will be towards the single, full screen
+ // task. We only need the transition spec for this task.
+
+ // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
+ // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ || activityType == ACTIVITY_TYPE_ASSISTANT
+ || windowingMode == WINDOWING_MODE_UNDEFINED) {
+ List<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ if (taskView == null) {
+ specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
+ } else {
+ mTmpTransform.fillIn(taskView);
+ stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
+ AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, taskView,
+ mTmpTransform, true /* addHeaderBitmap */);
+ if (spec != null) {
+ specs.add(spec);
+ }
+ }
+ return specs;
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Composes a single animation spec for the given {@link Task}
+ */
+ private static AppTransitionAnimationSpec composeOffscreenAnimationSpec(Task task,
+ Rect taskRect) {
+ return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
+ }
+
+ public static GraphicBuffer composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
+ float scale = transform.scale;
+ int fromWidth = (int) (transform.rect.width() * scale);
+ int fromHeight = (int) (transform.rect.height() * scale);
+ if (fromWidth == 0 || fromHeight == 0) {
+ Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
+ " at transform: " + transform);
+
+ return RecentsTransition.drawViewIntoGraphicBuffer(1, 1, null, 1f, 0x00ffffff);
+ } else {
+ if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+ return RecentsTransition.drawViewIntoGraphicBuffer(fromWidth, fromHeight, null, 1f,
+ 0xFFff0000);
+ } else {
+ return RecentsTransition.drawViewIntoGraphicBuffer(fromWidth, fromHeight, taskView,
+ scale, 0);
+ }
+ }
+ }
+
+ private static GraphicBuffer composeHeaderBitmap(TaskView taskView,
+ TaskViewTransform transform) {
+ float scale = transform.scale;
+ int headerWidth = (int) (transform.rect.width());
+ int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
+ if (headerWidth == 0 || headerHeight == 0) {
+ return null;
+ }
+
+ if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+ return RecentsTransition.drawViewIntoGraphicBuffer(headerWidth, headerHeight, null, 1f,
+ 0xFFff0000);
+ } else {
+ return RecentsTransition.drawViewIntoGraphicBuffer(headerWidth, headerHeight,
+ taskView.mHeaderView, scale, 0);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
deleted file mode 100644
index 7442904ec1c0..000000000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-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;
-
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.ActivityOptions.OnAnimationStartedListener;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IRemoteCallback;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.AppTransitionAnimationSpec;
-import android.view.DisplayListCanvas;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.RenderNode;
-import android.view.ThreadedRenderer;
-import android.view.View;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A helper class to create transitions to/from Recents
- */
-public class RecentsTransitionHelper {
-
- private static final String TAG = "RecentsTransitionHelper";
- private static final boolean DEBUG = false;
-
- /**
- * Special value for {@link #mAppTransitionAnimationSpecs}: Indicate that we are currently
- * waiting for the specs to be retrieved.
- */
- private static final List<AppTransitionAnimationSpec> SPECS_WAITING = new ArrayList<>();
-
- @GuardedBy("this")
- private List<AppTransitionAnimationSpec> mAppTransitionAnimationSpecs = SPECS_WAITING;
-
- private Context mContext;
- private Handler mHandler;
- private TaskViewTransform mTmpTransform = new TaskViewTransform();
-
- private class StartScreenPinningRunnableRunnable implements Runnable {
-
- private int taskId = -1;
-
- @Override
- public void run() {
- EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext, taskId));
- }
- }
- private StartScreenPinningRunnableRunnable mStartScreenPinningRunnable
- = new StartScreenPinningRunnableRunnable();
-
- public RecentsTransitionHelper(Context context) {
- mContext = context;
- mHandler = new Handler();
- }
-
- /**
- * Launches the specified {@link Task}.
- */
- public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
- final TaskStackView stackView, final TaskView taskView,
- final boolean screenPinningRequested, final int windowingMode, final int activityType) {
-
- final ActivityOptions.OnAnimationStartedListener animStartedListener;
- final AppTransitionAnimationSpecsFuture transitionFuture;
- if (taskView != null) {
-
- // Fetch window rect here already in order not to be blocked on lock contention in WM
- // when the future calls it.
- final Rect windowRect = Recents.getSystemServices().getWindowRect();
- transitionFuture = getAppTransitionFuture(() -> composeAnimationSpecs(
- task, stackView, windowingMode, activityType, windowRect));
- animStartedListener = new OnAnimationStartedListener() {
- private boolean mHandled;
-
- @Override
- public void onAnimationStarted() {
- if (mHandled) {
- return;
- }
- mHandled = true;
-
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- stackView.cancelAllTaskViewAnimations();
-
- if (screenPinningRequested) {
- // Request screen pinning after the animation runs
- mStartScreenPinningRunnable.taskId = task.key.id;
- mHandler.postDelayed(mStartScreenPinningRunnable, 350);
- }
-
- if (!Recents.getConfiguration().isLowRamDevice) {
- // Reset the state where we are waiting for the transition to start
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
- }
- }
- };
- } else {
- // This is only the case if the task is not on screen (scrolled offscreen for example)
- transitionFuture = null;
- animStartedListener = new OnAnimationStartedListener() {
- private boolean mHandled;
-
- @Override
- public void onAnimationStarted() {
- if (mHandled) {
- return;
- }
- mHandled = true;
-
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- stackView.cancelAllTaskViewAnimations();
-
- if (!Recents.getConfiguration().isLowRamDevice) {
- // Reset the state where we are waiting for the transition to start
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
- }
- }
- };
- }
-
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
- final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
- mHandler, transitionFuture != null ? transitionFuture.future : null,
- animStartedListener, true /* scaleUp */);
- if (taskView == null) {
- // If there is no task view, then we do not need to worry about animating out occluding
- // task views, and we can launch immediately
- startTaskActivity(stack, task, taskView, opts, transitionFuture,
- windowingMode, activityType);
- } else {
- LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
- screenPinningRequested);
- EventBus.getDefault().send(launchStartedEvent);
- startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode,
- activityType);
- }
- Recents.getSystemServices().sendCloseSystemWindows(
- StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
- }
-
- public IRemoteCallback wrapStartedListener(final OnAnimationStartedListener listener) {
- if (listener == null) {
- return null;
- }
- return new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- listener.onAnimationStarted();
- }
- });
- }
- };
- }
-
- /**
- * Starts the activity for the launch task.
- *
- * @param taskView this is the {@link TaskView} that we are launching from. This can be null if
- * we are toggling recents and the launch-to task is now offscreen.
- */
- private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
- ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
- int windowingMode, int activityType) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(mContext, task.key, task.title, opts, windowingMode,
- activityType,
- succeeded -> {
- if (succeeded) {
- // Keep track of the index of the task launch
- int taskIndexFromFront = 0;
- int taskIndex = stack.indexOfStackTask(task);
- if (taskIndex > -1) {
- taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
- }
- EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
- } else {
- // Dismiss the task if we fail to launch it
- if (taskView != null) {
- taskView.dismissTask();
- }
-
- // Keep track of failed launches
- EventBus.getDefault().send(new LaunchTaskFailedEvent());
- }
- });
- if (transitionFuture != null) {
- mHandler.post(transitionFuture::precacheSpecs);
- }
- }
-
- /**
- * Creates a future which will later be queried for animation specs for this current transition.
- *
- * @param composer The implementation that composes the specs on the UI thread.
- */
- public AppTransitionAnimationSpecsFuture getAppTransitionFuture(
- final AnimationSpecComposer composer) {
- synchronized (this) {
- mAppTransitionAnimationSpecs = SPECS_WAITING;
- }
- IAppTransitionAnimationSpecsFuture future = new IAppTransitionAnimationSpecsFuture.Stub() {
- @Override
- public AppTransitionAnimationSpec[] get() throws RemoteException {
- mHandler.post(() -> {
- synchronized (RecentsTransitionHelper.this) {
- mAppTransitionAnimationSpecs = composer.composeSpecs();
- RecentsTransitionHelper.this.notifyAll();
- }
- });
- synchronized (RecentsTransitionHelper.this) {
- while (mAppTransitionAnimationSpecs == SPECS_WAITING) {
- try {
- RecentsTransitionHelper.this.wait();
- } catch (InterruptedException e) {}
- }
- if (mAppTransitionAnimationSpecs == null) {
- return null;
- }
- AppTransitionAnimationSpec[] specs
- = new AppTransitionAnimationSpec[mAppTransitionAnimationSpecs.size()];
- mAppTransitionAnimationSpecs.toArray(specs);
- mAppTransitionAnimationSpecs = SPECS_WAITING;
- return specs;
- }
- }
- };
- return new AppTransitionAnimationSpecsFuture(composer, future);
- }
-
- /**
- * Composes the transition spec when docking a task, which includes a full task bitmap.
- */
- public List<AppTransitionAnimationSpec> composeDockAnimationSpec(TaskView taskView,
- Rect bounds) {
- mTmpTransform.fillIn(taskView);
- Task task = taskView.getTask();
- GraphicBuffer buffer = RecentsTransitionHelper.composeTaskBitmap(taskView, mTmpTransform);
- return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, buffer,
- bounds));
- }
-
- /**
- * Composes the animation specs for all the tasks in the target stack.
- */
- private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
- final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
- // Calculate the offscreen task rect (for tasks that are not backed by views)
- TaskView taskView = stackView.getChildViewForTask(task);
- TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
- Rect offscreenTaskRect = new Rect();
- stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
-
- // If this is a full screen stack, the transition will be towards the single, full screen
- // task. We only need the transition spec for this task.
-
- // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
- // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
- if (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- || activityType == ACTIVITY_TYPE_ASSISTANT
- || windowingMode == WINDOWING_MODE_UNDEFINED) {
- List<AppTransitionAnimationSpec> specs = new ArrayList<>();
- if (taskView == null) {
- specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
- } else {
- mTmpTransform.fillIn(taskView);
- stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
- AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, taskView,
- mTmpTransform, true /* addHeaderBitmap */);
- if (spec != null) {
- specs.add(spec);
- }
- }
- return specs;
- }
- return Collections.emptyList();
- }
-
- /**
- * Composes a single animation spec for the given {@link Task}
- */
- private static AppTransitionAnimationSpec composeOffscreenAnimationSpec(Task task,
- Rect taskRect) {
- return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
- }
-
- public static GraphicBuffer composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
- float scale = transform.scale;
- int fromWidth = (int) (transform.rect.width() * scale);
- int fromHeight = (int) (transform.rect.height() * scale);
- if (fromWidth == 0 || fromHeight == 0) {
- Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
- " at transform: " + transform);
-
- return drawViewIntoGraphicBuffer(1, 1, null, 1f, 0x00ffffff);
- } else {
- if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return drawViewIntoGraphicBuffer(fromWidth, fromHeight, null, 1f, 0xFFff0000);
- } else {
- return drawViewIntoGraphicBuffer(fromWidth, fromHeight, taskView, scale, 0);
- }
- }
- }
-
- private static GraphicBuffer composeHeaderBitmap(TaskView taskView,
- TaskViewTransform transform) {
- float scale = transform.scale;
- int headerWidth = (int) (transform.rect.width());
- int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
- if (headerWidth == 0 || headerHeight == 0) {
- return null;
- }
-
- if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return drawViewIntoGraphicBuffer(headerWidth, headerHeight, null, 1f, 0xFFff0000);
- } else {
- return drawViewIntoGraphicBuffer(headerWidth, headerHeight, taskView.mHeaderView,
- scale, 0);
- }
- }
-
- public static GraphicBuffer drawViewIntoGraphicBuffer(int bufferWidth, int bufferHeight,
- View view, float scale, int eraseColor) {
- RenderNode node = RenderNode.create("RecentsTransition", null);
- node.setLeftTopRightBottom(0, 0, bufferWidth, bufferHeight);
- node.setClipToBounds(false);
- DisplayListCanvas c = node.start(bufferWidth, bufferHeight);
- c.scale(scale, scale);
- if (eraseColor != 0) {
- c.drawColor(eraseColor);
- }
- if (view != null) {
- view.draw(c);
- }
- node.end(c);
- Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, bufferWidth, bufferHeight);
- return hwBitmap.createGraphicBufferHandle();
- }
-
- /**
- * Composes a single animation spec for the given {@link TaskView}
- */
- private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
- TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
- GraphicBuffer b = null;
- if (addHeaderBitmap) {
- b = composeHeaderBitmap(taskView, transform);
- if (b == null) {
- return null;
- }
- }
-
- Rect taskRect = new Rect();
- transform.rect.round(taskRect);
- // Disable in for low ram devices because each task does in Recents does not have fullscreen
- // height (stackView height) and when transitioning to fullscreen app, the code below would
- // force the task thumbnail to full stackView height immediately causing the transition
- // jarring.
- if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() !=
- stackView.getStack().getStackFrontMostTask()) {
- taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
- }
- return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
- }
-
- public interface AnimationSpecComposer {
- List<AppTransitionAnimationSpec> composeSpecs();
- }
-
- /**
- * Class to be returned from {@link #composeAnimationSpec} that gives access to both the future
- * and the anonymous class used for composing.
- */
- public class AppTransitionAnimationSpecsFuture {
-
- private final AnimationSpecComposer composer;
- private final IAppTransitionAnimationSpecsFuture future;
-
- private AppTransitionAnimationSpecsFuture(AnimationSpecComposer composer,
- IAppTransitionAnimationSpecsFuture future) {
- this.composer = composer;
- this.future = future;
- }
-
- public IAppTransitionAnimationSpecsFuture getFuture() {
- return future;
- }
-
- /**
- * Manually generates and caches the spec such that they are already available when the
- * future needs.
- */
- public void precacheSpecs() {
- synchronized (RecentsTransitionHelper.this) {
- mAppTransitionAnimationSpecs = composer.composeSpecs();
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 5f12a04b5e4d..f4973d023e76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,8 +16,13 @@
package com.android.systemui.recents.views;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY;
+import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
+
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -28,6 +33,7 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.MathUtils;
@@ -54,15 +60,22 @@ import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
import com.android.systemui.recents.events.component.ExpandPipEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
@@ -76,8 +89,9 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
-import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -101,6 +115,7 @@ public class RecentsView extends FrameLayout {
private static final int BUSY_RECENTS_TASK_COUNT = 3;
+ private Handler mHandler;
private TaskStackView mTaskStackView;
private TextView mStackActionButton;
private TextView mEmptyView;
@@ -126,7 +141,7 @@ public class RecentsView extends FrameLayout {
mMultiWindowBackgroundScrim.setAlpha(alpha);
};
- private RecentsTransitionHelper mTransitionHelper;
+ private RecentsTransitionComposer mTransitionHelper;
@ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
private RecentsViewTouchHandler mTouchHandler;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -148,7 +163,8 @@ public class RecentsView extends FrameLayout {
setWillNotDraw(false);
SystemServicesProxy ssp = Recents.getSystemServices();
- mTransitionHelper = new RecentsTransitionHelper(getContext());
+ mHandler = new Handler();
+ mTransitionHelper = new RecentsTransitionComposer(getContext());
mDividerSize = ssp.getDockedDividerSize(context);
mTouchHandler = new RecentsViewTouchHandler(this);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
@@ -518,9 +534,8 @@ public class RecentsView extends FrameLayout {
/**** EventBus Events ****/
public final void onBusEvent(LaunchTaskEvent event) {
- mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
- event.taskView, event.screenPinningRequested, event.targetWindowingMode,
- event.targetActivityType);
+ launchTaskFromRecents(getStack(), event.task, mTaskStackView, event.taskView,
+ event.screenPinningRequested, event.targetWindowingMode, event.targetActivityType);
if (Recents.getConfiguration().isLowRamDevice) {
EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */));
}
@@ -607,17 +622,15 @@ public class RecentsView extends FrameLayout {
};
final Rect taskRect = getTaskRect(event.taskView);
- AppTransitionAnimationSpecsFuture future =
- mTransitionHelper.getAppTransitionFuture(
- new AnimationSpecComposer() {
- @Override
- public List<AppTransitionAnimationSpec> composeSpecs() {
- return mTransitionHelper.composeDockAnimationSpec(
- event.taskView, taskRect);
- }
- });
+ AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(
+ getHandler()) {
+ @Override
+ public List<AppTransitionAnimationSpec> composeSpecs() {
+ return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect);
+ }
+ };
ssp.overridePendingAppTransitionMultiThumbFuture(future.getFuture(),
- mTransitionHelper.wrapStartedListener(startedListener),
+ RecentsTransition.wrapStartedListener(getHandler(), startedListener),
true /* scaleUp */);
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
@@ -881,7 +894,8 @@ public class RecentsView extends FrameLayout {
* @return the bounds of the stack action button.
*/
Rect getStackActionButtonBoundsFromStackLayout() {
- Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
+ Rect actionButtonRect = new Rect(
+ mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
int left, top;
if (Recents.getConfiguration().isLowRamDevice) {
Rect windowRect = Recents.getSystemServices().getWindowRect();
@@ -906,6 +920,140 @@ public class RecentsView extends FrameLayout {
return mStackActionButton;
}
+ /**
+ * Launches the specified {@link Task}.
+ */
+ public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
+ final TaskStackView stackView, final TaskView taskView,
+ final boolean screenPinningRequested, final int windowingMode, final int activityType) {
+
+ final ActivityOptions.OnAnimationStartedListener animStartedListener;
+ final AppTransitionAnimationSpecsFuture transitionFuture;
+ if (taskView != null) {
+
+ // Fetch window rect here already in order not to be blocked on lock contention in WM
+ // when the future calls it.
+ final Rect windowRect = Recents.getSystemServices().getWindowRect();
+ transitionFuture = new AppTransitionAnimationSpecsFuture(stackView.getHandler()) {
+ @Override
+ public List<AppTransitionAnimationSpec> composeSpecs() {
+ return mTransitionHelper.composeAnimationSpecs(task, stackView, windowingMode,
+ activityType, windowRect);
+ }
+ };
+ animStartedListener = new OnAnimationStartedListener() {
+ private boolean mHandled;
+
+ @Override
+ public void onAnimationStarted() {
+ if (mHandled) {
+ return;
+ }
+ mHandled = true;
+
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ stackView.cancelAllTaskViewAnimations();
+
+ if (screenPinningRequested) {
+ // Request screen pinning after the animation runs
+ mHandler.postDelayed(() -> {
+ EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext,
+ task.key.id));
+ }, 350);
+ }
+
+ if (!Recents.getConfiguration().isLowRamDevice) {
+ // Reset the state where we are waiting for the transition to start
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+ }
+ }
+ };
+ } else {
+ // This is only the case if the task is not on screen (scrolled offscreen for example)
+ transitionFuture = null;
+ animStartedListener = new OnAnimationStartedListener() {
+ private boolean mHandled;
+
+ @Override
+ public void onAnimationStarted() {
+ if (mHandled) {
+ return;
+ }
+ mHandled = true;
+
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ stackView.cancelAllTaskViewAnimations();
+
+ if (!Recents.getConfiguration().isLowRamDevice) {
+ // Reset the state where we are waiting for the transition to start
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+ }
+ }
+ };
+ }
+
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
+ final ActivityOptions opts = RecentsTransition.createAspectScaleAnimation(mContext,
+ mHandler, true /* scaleUp */, transitionFuture != null ? transitionFuture : null,
+ animStartedListener);
+ if (taskView == null) {
+ // If there is no task view, then we do not need to worry about animating out occluding
+ // task views, and we can launch immediately
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ windowingMode, activityType);
+ } else {
+ LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
+ screenPinningRequested);
+ EventBus.getDefault().send(launchStartedEvent);
+ startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode,
+ activityType);
+ }
+ ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
+ }
+
+ /**
+ * Starts the activity for the launch task.
+ *
+ * @param taskView this is the {@link TaskView} that we are launching from. This can be null if
+ * we are toggling recents and the launch-to task is now offscreen.
+ */
+ private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
+ ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
+ int windowingMode, int activityType) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startActivityFromRecents(mContext, task.key, task.title, opts, windowingMode,
+ activityType,
+ succeeded -> {
+ if (succeeded) {
+ // Keep track of the index of the task launch
+ int taskIndexFromFront = 0;
+ int taskIndex = stack.indexOfStackTask(task);
+ if (taskIndex > -1) {
+ taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
+ }
+ EventBus.getDefault().send(new LaunchTaskSucceededEvent(
+ taskIndexFromFront));
+ } else {
+ // Dismiss the task if we fail to launch it
+ if (taskView != null) {
+ taskView.dismissTask();
+ }
+
+ // Keep track of failed launches
+ EventBus.getDefault().send(new LaunchTaskFailedEvent());
+ }
+ });
+ if (transitionFuture != null) {
+ mHandler.post(transitionFuture::composeSpecsSynchronous);
+ }
+ }
+
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
super.requestDisallowInterceptTouchEvent(disallowIntercept);
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 195f4d3f480d..1cda30111db8 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -16,8 +16,8 @@
package com.android.systemui.shortcut;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.os.UserHandle.USER_CURRENT;
import android.app.ActivityManager;
@@ -92,8 +92,8 @@ public class ShortcutKeyDispatcher extends SystemUI
// If there is no window docked, we dock the top-most window.
Recents recents = getComponent(Recents.class);
int dockMode = (shortcutCode == SC_DOCK_LEFT)
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+ : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
List<ActivityManager.RecentTaskInfo> taskList =
ActivityManagerWrapper.getInstance().getRecentTasks(1, USER_CURRENT);
recents.showRecentApps(
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 7bcef574cfd6..1596d120c16f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -439,7 +439,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mMinimizedSnapAlgorithm == null) {
mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(),
- mStableInsets, mDockedStackMinimized && mHomeStackResizable);
+ mStableInsets, mDockSide, mDockedStackMinimized && mHomeStackResizable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index 0997983ae73f..b2bbe307d8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -31,8 +31,8 @@ import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
import com.android.systemui.recents.events.component.ShowUserToastEvent;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
import com.android.systemui.stackdivider.events.StartedDragingEvent;
import com.android.systemui.stackdivider.events.StoppedDragingEvent;
@@ -76,7 +76,7 @@ public class ForcedResizableInfoActivityController {
mContext = context;
EventBus.getDefault().register(this);
SystemServicesProxy.getInstance(context).registerTaskStackListener(
- new TaskStackChangeListener() {
+ new SysUiTaskStackChangeListener() {
@Override
public void onActivityForcedResizable(String packageName, int taskId,
int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
new file mode 100644
index 000000000000..5090f74d4019
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.WirelessUtils;
+import com.android.systemui.DemoMode;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
+
+import java.util.List;
+
+public class OperatorNameView extends TextView implements DemoMode, DarkReceiver,
+ SignalCallback, Tunable {
+
+ private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
+
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private boolean mDemoMode;
+
+ private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshCarrierInfo() {
+ updateText();
+ }
+ };
+
+ public OperatorNameView(Context context) {
+ this(context, null);
+ }
+
+ public OperatorNameView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public OperatorNameView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ mKeyguardUpdateMonitor.registerCallback(mCallback);
+ Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
+ Dependency.get(NetworkController.class).addCallback(this);
+ Dependency.get(TunerService.class).addTunable(this, KEY_SHOW_OPERATOR_NAME);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mKeyguardUpdateMonitor.removeCallback(mCallback);
+ Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
+ Dependency.get(NetworkController.class).removeCallback(this);
+ Dependency.get(TunerService.class).removeTunable(this);
+ }
+
+ @Override
+ public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+ setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+ }
+
+ @Override
+ public void setIsAirplaneMode(IconState icon) {
+ update();
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ update();
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ if (!mDemoMode && command.equals(COMMAND_ENTER)) {
+ mDemoMode = true;
+ } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
+ mDemoMode = false;
+ update();
+ } else if (mDemoMode && command.equals(COMMAND_OPERATOR)) {
+ setText(args.getString("name"));
+ }
+ }
+
+ private void update() {
+ boolean showOperatorName = Dependency.get(TunerService.class)
+ .getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0;
+ setVisibility(showOperatorName ? VISIBLE : GONE);
+
+ boolean hasMobile = ConnectivityManager.from(mContext)
+ .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext);
+ if (!hasMobile || airplaneMode) {
+ setText(null);
+ setVisibility(GONE);
+ return;
+ }
+
+ if (!mDemoMode) {
+ updateText();
+ }
+ }
+
+ private void updateText() {
+ CharSequence displayText = null;
+ List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+ final int N = subs.size();
+ for (int i = 0; i < N; i++) {
+ int subId = subs.get(i).getSubscriptionId();
+ State simState = mKeyguardUpdateMonitor.getSimState(subId);
+ CharSequence carrierName = subs.get(i).getCarrierName();
+ if (!TextUtils.isEmpty(carrierName) && simState == State.READY) {
+ ServiceState ss = mKeyguardUpdateMonitor.getServiceState(subId);
+ if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) {
+ displayText = carrierName;
+ break;
+ }
+ }
+ }
+
+ setText(displayText);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index fed2ebe9c14f..7022c479eae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -45,8 +45,8 @@ import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
@@ -305,10 +305,10 @@ public class CarStatusBar extends StatusBar implements
}
/**
- * An implementation of TaskStackChangeListener, that listens for changes in the system task
+ * An implementation of SysUiTaskStackChangeListener, that listens for changes in the system task
* stack and notifies the navigation bar.
*/
- private class TaskStackListenerImpl extends TaskStackChangeListener {
+ private class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
@Override
public void onTaskStackChanged() {
SystemServicesProxy ssp = Recents.getSystemServices();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 2c3f452e8274..61f3130b9be4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -60,6 +60,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private StatusBar mStatusBarComponent;
private DarkIconManager mDarkIconManager;
private SignalClusterView mSignalClusterView;
+ private View mOperatorNameFrame;
private SignalCallback mSignalCallback = new SignalCallback() {
@Override
@@ -97,6 +98,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
// Default to showing until we know otherwise.
showSystemIconArea(false);
initEmergencyCryptkeeperText();
+ initOperatorName();
}
@Override
@@ -150,8 +152,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
if ((diff1 & DISABLE_SYSTEM_INFO) != 0) {
if ((state1 & DISABLE_SYSTEM_INFO) != 0) {
hideSystemIconArea(animate);
+ hideOperatorName(animate);
} else {
showSystemIconArea(animate);
+ showOperatorName(animate);
}
}
if ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0) {
@@ -207,6 +211,18 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
animateShow(mNotificationIconAreaInner, animate);
}
+ public void hideOperatorName(boolean animate) {
+ if (mOperatorNameFrame != null) {
+ animateHide(mOperatorNameFrame, animate);
+ }
+ }
+
+ public void showOperatorName(boolean animate) {
+ if (mOperatorNameFrame != null) {
+ animateShow(mOperatorNameFrame, animate);
+ }
+ }
+
/**
* Hides a view.
*/
@@ -268,4 +284,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
parent.removeView(emergencyViewStub);
}
}
+
+ private void initOperatorName() {
+ if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) {
+ ViewStub stub = mStatusBar.findViewById(R.id.operator_name);
+ mOperatorNameFrame = stub.inflate();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index ee9a791585c4..8e0a506ac52d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -26,7 +26,6 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.systemui.Dependency;
@@ -206,7 +205,7 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
&& mDivider.getView().getWindowManagerProxy().getDockSide() == DOCKED_INVALID) {
Rect initialBounds = null;
int dragMode = calculateDragMode();
- int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ int createMode = ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
if (dragMode == DRAG_MODE_DIVIDER) {
initialBounds = new Rect();
mDivider.getView().calculateBoundsForPosition(mIsVertical
@@ -218,10 +217,10 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
initialBounds);
} else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX
< mContext.getResources().getDisplayMetrics().widthPixels / 2) {
- createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ createMode = ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
}
- boolean docked = mRecentsComponent.dockTopTask(dragMode, createMode, initialBounds,
- MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
+ boolean docked = mRecentsComponent.splitPrimaryTask(dragMode, createMode,
+ initialBounds, MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
if (docked) {
mDragMode = dragMode;
if (mDragMode == DRAG_MODE_DIVIDER) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b876286b9ba2..ba4ff585fed7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -65,8 +65,8 @@ import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -768,7 +768,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
- private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
+ private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
// Listen for changes to stacks and then check which instant apps are foreground.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 9f039543e3e6..fa34d4a62d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
@@ -1538,8 +1539,8 @@ public class StatusBar extends SystemUI implements DemoMode,
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
- ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
+ return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
+ ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
} else {
Divider divider = getComponent(Divider.class);
if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
@@ -4128,6 +4129,9 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
}
+ if (modeChange || command.equals(COMMAND_OPERATOR)) {
+ dispatchDemoCommandToView(command, args, R.id.operator_name);
+ }
}
private void dispatchDemoCommandToView(String command, Bundle args, int id) {
@@ -4515,12 +4519,14 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean useDarkTheme = systemColors != null
&& (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
if (isUsingDarkTheme() != useDarkTheme) {
- try {
- mOverlayManager.setEnabled("com.android.systemui.theme.dark",
- useDarkTheme, mCurrentUserId);
- } catch (RemoteException e) {
- Log.w(TAG, "Can't change theme", e);
- }
+ mUiOffloadThread.submit(() -> {
+ try {
+ mOverlayManager.setEnabled("com.android.systemui.theme.dark",
+ useDarkTheme, mCurrentUserId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't change theme", e);
+ }
+ });
}
// Lock wallpaper defines the color of the majority of the views, hence we'll use it
@@ -5260,7 +5266,8 @@ public class StatusBar extends SystemUI implements DemoMode,
boolean isCameraAllowedByAdmin() {
if (mDevicePolicyManager.getCameraDisabled(null, mCurrentUserId)) {
return false;
- } else if (isKeyguardShowing() && isKeyguardSecure()) {
+ } else if (mStatusBarKeyguardViewManager == null ||
+ (isKeyguardShowing() && isKeyguardSecure())) {
// Check if the admin has disabled the camera specifically for the keyguard
return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUserId)
& DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
@@ -7091,7 +7098,9 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
- return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
+ return mShowLockscreenNotifications
+ && ((mDisabled2 & DISABLE2_NOTIFICATION_SHADE) == 0)
+ && !mNotificationData.isAmbient(sbn.getKey());
}
// extended in StatusBar
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index b86fc214129c..d7583149ccc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -20,7 +20,6 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.argThat;
@@ -41,10 +40,8 @@ import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.keyguard.WorkLockActivity;
-import com.android.systemui.keyguard.WorkLockActivityController;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.TaskStackChangeListener;
import org.junit.Before;
import org.junit.Test;
@@ -68,7 +65,7 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
private @Mock IActivityManager mIActivityManager;
private WorkLockActivityController mController;
- private TaskStackChangeListener mTaskStackListener;
+ private SysUiTaskStackChangeListener mTaskStackListener;
@Before
public void setUp() throws Exception {
@@ -78,8 +75,8 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
doReturn("com.example.test").when(mContext).getPackageName();
// Construct controller. Save the TaskStackListener for injecting events.
- final ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
- ArgumentCaptor.forClass(TaskStackChangeListener.class);
+ final ArgumentCaptor<SysUiTaskStackChangeListener> listenerCaptor =
+ ArgumentCaptor.forClass(SysUiTaskStackChangeListener.class);
mController =
new WorkLockActivityController(mContext, mSystemServicesProxy, mIActivityManager);
@@ -97,7 +94,7 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
// The overlay should start and the task the activity started in should not be removed.
verifyStartActivity(TASK_ID, true /*taskOverlay*/);
- verify(mSystemServicesProxy, never()).removeTask(anyInt() /*taskId*/);
+ verify(mIActivityManager, never()).removeTask(anyInt() /*taskId*/);
}
@Test
@@ -111,7 +108,7 @@ public class WorkLockActivityControllerTest extends SysuiTestCase {
// The task the activity started in should be removed to prevent the locked task from
// being shown.
verifyStartActivity(TASK_ID, true /*taskOverlay*/);
- verify(mSystemServicesProxy).removeTask(TASK_ID);
+ verify(mIActivityManager).removeTask(TASK_ID);
}
// End of tests, start of helpers
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 7b257ab468a6..fad6bd1b3f83 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4725,6 +4725,45 @@ message MetricsEvent {
// Tag FIELD_CLASS_NAME: Class name of the activity that is being disabled for autofill
AUTOFILL_SERVICE_DISABLED_ACTIVITY = 1232;
+ // ACTION: Stop an app and turn on background check
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACTION_APP_STOP_AND_BACKGROUND_CHECK = 1233;
+
+ // FIELD: The action type for each anomaly
+ // CATEGORY: SETTINGS
+ // OS: P
+ FIELD_ANOMALY_ACTION_TYPE = 1234;
+
+ // OPEN: Settings -> Battery -> Wakelock anomaly
+ // CATEGORY: SETTINGS
+ // OS: P
+ ANOMALY_TYPE_WAKELOCK = 1235;
+
+ // OPEN: Settings -> Battery -> Wakeup alarm anomaly
+ // CATEGORY: SETTINGS
+ // OS: P
+ ANOMALY_TYPE_WAKEUP_ALARM = 1236;
+
+ // OPEN: Settings -> Battery -> Unoptimized bt anomaly
+ // CATEGORY: SETTINGS
+ // OS: P
+ ANOMALY_TYPE_UNOPTIMIZED_BT = 1237;
+
+ // Open: Settings > Dev options > Oem unlock > lock it > warning dialog.
+ // OS: P
+ DIALOG_OEM_LOCK_INFO = 1238;
+
+ // ACTION: Settings > Wi-Fi > Click one network > Auto sign in
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACTION_WIFI_AUTO_SIGN_IN = 1239;
+
+ // Open: Settings > System > About phone > IMEI
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_IMEI_INFO = 1240;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index ee0c04331da2..9d25055d6d5a 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -673,6 +673,10 @@ message StaEvent {
// Framework initiated disconnect. Sometimes generated to give an extra reason for a disconnect
// Should typically be followed by a NETWORK_DISCONNECTION_EVENT with a local_gen = true
TYPE_FRAMEWORK_DISCONNECT = 15;
+
+ // The NetworkAgent score for wifi has changed in a way that may impact
+ // connectivity
+ TYPE_SCORE_BREACH = 16;
}
enum FrameworkDisconnectReason {
@@ -784,6 +788,9 @@ message StaEvent {
// Authentication failure reason, as reported by WifiManager (calculated from state & deauth code)
optional AuthFailureReason auth_failure_reason = 13 [default = AUTH_FAILURE_UNKNOWN];
+
+ // NetworkAgent score of connected wifi
+ optional int32 last_score = 14 [default = -1];
}
// Wi-Fi Aware metrics
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6c3eb200ef3d..caff0ba9bea2 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -447,7 +447,6 @@ public final class AutofillManagerService extends SystemService {
android.view.autofill.Helper.sDebug = debug;
}
-
private void setVerboseLocked(boolean verbose) {
com.android.server.autofill.Helper.sVerbose = verbose;
android.view.autofill.Helper.sVerbose = verbose;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 2ed5eeee89dd..d8eaccc13532 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -813,7 +813,23 @@ final class AutofillManagerServiceImpl {
synchronized (mLock) {
resetSession = resetClient || isClientSessionDestroyedLocked(client);
}
- client.setState(isEnabled(), resetSession, resetClient);
+ int flags = 0;
+ if (isEnabled()) {
+ flags |= AutofillManager.SET_STATE_FLAG_ENABLED;
+ }
+ if (resetSession) {
+ flags |= AutofillManager.SET_STATE_FLAG_RESET_SESSION;
+ }
+ if (resetClient) {
+ flags |= AutofillManager.SET_STATE_FLAG_RESET_CLIENT;
+ }
+ if (sDebug) {
+ flags |= AutofillManager.SET_STATE_FLAG_DEBUG;
+ }
+ if (sVerbose) {
+ flags |= AutofillManager.SET_STATE_FLAG_VERBOSE;
+ }
+ client.setState(flags);
} catch (RemoteException re) {
/* ignore */
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 236fbfd94f53..02a62e10e111 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,6 +28,7 @@ import android.view.autofill.AutofillValue;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@@ -112,4 +113,12 @@ public final class Helper {
}
return log;
}
+
+ public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable String text) {
+ if (text == null) {
+ pw.println("null");
+ } else {
+ pw.print(text.length()); pw.println("_chars");
+ }
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 010995f2c33b..3564432e6b93 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -16,10 +16,10 @@
package com.android.server.autofill;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
-import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS;
-import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
@@ -43,6 +43,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.graphics.Bitmap;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -74,11 +75,11 @@ import android.view.autofill.IAutoFillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
import com.android.internal.R;
+import com.android.internal.app.IAssistDataReceiver;
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.HandlerCaller;
-import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
@@ -203,16 +204,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Receiver of assist data from the app's {@link Activity}.
*/
- private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
+ private final IAssistDataReceiver mAssistReceiver = new IAssistDataReceiver.Stub() {
@Override
- public void send(int resultCode, Bundle resultData) throws RemoteException {
- final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
+ public void onHandleAssistData(Bundle resultData) throws RemoteException {
+ final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE);
if (structure == null) {
Slog.e(TAG, "No assist structure - app might have crashed providing it");
return;
}
- final Bundle receiverExtras = resultData.getBundle(KEY_RECEIVER_EXTRAS);
+ final Bundle receiverExtras = resultData.getBundle(ASSIST_KEY_RECEIVER_EXTRAS);
if (receiverExtras == null) {
Slog.e(TAG, "No receiver extras - app might have crashed providing it");
return;
@@ -261,6 +262,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mRemoteFillService.onFillRequest(request);
}
+
+ @Override
+ public void onHandleAssistScreenshot(Bitmap screenshot) {
+ // Do nothing
+ }
};
/**
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 6d3d792e8d31..dac4586fbd9d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -49,6 +49,8 @@ import android.widget.RemoteViews;
import com.android.internal.R;
import com.android.server.UiThread;
+import com.android.server.autofill.Helper;
+
import libcore.util.Objects;
import java.io.PrintWriter;
@@ -466,7 +468,8 @@ final class FillUi {
pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter != null);
- pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText);
+ pw.print(prefix); pw.print("mFilterText: ");
+ Helper.printlnRedactedText(pw, mFilterText);
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 68546bd22221..ad308975e613 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1163,8 +1163,10 @@ public final class BatteryService extends SystemService {
Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
private final IServiceNotification mNotification = new Notification();
+ // These variables are fixed after init.
private Callback mCallback;
private IHealthSupplier mHealthSupplier;
+ private String mInstanceName;
private final Object mLastServiceSetLock = new Object();
// Last IHealth service received.
@@ -1206,19 +1208,21 @@ public final class BatteryService extends SystemService {
IServiceManager manager = managerSupplier.get();
for (String name : sAllInstances) {
- if (manager.getTransport(IHealth.kInterfaceName, name) ==
+ if (manager.getTransport(IHealth.kInterfaceName, name) !=
IServiceManager.Transport.EMPTY) {
- continue;
+ mInstanceName = name;
+ break;
}
+ }
- manager.registerForNotifications(IHealth.kInterfaceName, name, mNotification);
- Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + name);
- return;
+ if (mInstanceName == null) {
+ throw new NoSuchElementException(String.format(
+ "No IHealth service instance among %s is available. Perhaps no permission?",
+ sAllInstances.toString()));
}
- throw new NoSuchElementException(String.format(
- "No IHealth service instance among %s is available. Perhaps no permission?",
- sAllInstances.toString()));
+ manager.registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification);
+ Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
}
interface Callback {
@@ -1258,7 +1262,7 @@ public final class BatteryService extends SystemService {
public final void onRegistration(String interfaceName, String instanceName,
boolean preexisting) {
if (!IHealth.kInterfaceName.equals(interfaceName)) return;
- if (!sAllInstances.contains(instanceName)) return;
+ if (!mInstanceName.equals(instanceName)) return;
try {
// ensures the order of multiple onRegistration on different threads.
synchronized (mLastServiceSetLock) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 8d46d1e27235..35f83e41071c 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -60,9 +60,6 @@ public class Watchdog extends Thread {
// Set this to true to use debug default values.
static final boolean DB = false;
- // Set this to true to have the watchdog record kernel thread stacks when it fires
- static final boolean RECORD_KERNEL_THREADS = true;
-
// Note 1: Do not lower this value below thirty seconds without tightening the invoke-with
// timeout in com.android.internal.os.ZygoteConnection, or wrapped applications
// can trigger the watchdog.
@@ -509,11 +506,6 @@ public class Watchdog extends Thread {
// The system's been hanging for a minute, another second or two won't hurt much.
SystemClock.sleep(2000);
- // Pull our own kernel thread stacks as well if we're configured for that
- if (RECORD_KERNEL_THREADS) {
- dumpKernelStackTraces();
- }
-
// Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
doSysRq('w');
doSysRq('l');
@@ -591,18 +583,6 @@ public class Watchdog extends Thread {
}
}
- private File dumpKernelStackTraces() {
- String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
- if (tracesPath == null || tracesPath.length() == 0) {
- return null;
- }
-
- native_dumpKernelStacks(tracesPath);
- return new File(tracesPath);
- }
-
- private native void native_dumpKernelStacks(String tracesPath);
-
public static final class OpenFdMonitor {
/**
* Number of FDs below the soft limit that we trigger a runtime restart at. This was
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f2e049325497..d2d9aab84065 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
@@ -25,10 +26,16 @@ import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -37,6 +44,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
@@ -331,7 +339,6 @@ import android.provider.Downloads;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
-import android.service.voice.VoiceInteractionSession;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -366,6 +373,7 @@ import com.android.internal.app.AssistUtils;
import com.android.internal.app.DumpHeapActivity;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
@@ -782,7 +790,7 @@ public class ActivityManagerService extends IActivityManager.Stub
public final Bundle extras;
public final Intent intent;
public final String hint;
- public final IResultReceiver receiver;
+ public final IAssistDataReceiver receiver;
public final int userHandle;
public boolean haveResult = false;
public Bundle result = null;
@@ -791,7 +799,8 @@ public class ActivityManagerService extends IActivityManager.Stub
public Bundle receiverExtras;
public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
- String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _userHandle) {
+ String _hint, IAssistDataReceiver _receiver, Bundle _receiverExtras,
+ int _userHandle) {
activity = _activity;
extras = _extras;
intent = _intent;
@@ -812,8 +821,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- final ArrayList<PendingAssistExtras> mPendingAssistExtras
- = new ArrayList<PendingAssistExtras>();
+ final ArrayList<PendingAssistExtras> mPendingAssistExtras = new ArrayList<>();
/**
* Process management.
@@ -3061,7 +3069,7 @@ public class ActivityManagerService extends IActivityManager.Stub
public void batterySendBroadcast(Intent intent) {
synchronized (this) {
broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- AppOpsManager.OP_NONE, null, false, false,
+ OP_NONE, null, false, false,
-1, SYSTEM_UID, UserHandle.USER_ALL);
}
}
@@ -4035,10 +4043,14 @@ public class ActivityManagerService extends IActivityManager.Stub
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
"updateUsageStats: comp=" + component + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
+ component.userId, component.realActivity.getPackageName(),
+ component.realActivity.getShortClassName(), resumed ? 1 : 0);
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
UsageEvents.Event.MOVE_TO_FOREGROUND);
+
}
synchronized (stats) {
stats.noteActivityResumedLocked(component.app.uid);
@@ -4083,7 +4095,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instr == null) {
- intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
// For ANR debugging to verify if the user activity is the one that actually
// launched.
@@ -4155,7 +4167,7 @@ public class ActivityManagerService extends IActivityManager.Stub
String lastVers = Settings.Secure.getString(
resolver, Settings.Secure.LAST_SETUP_SHOWN);
if (vers != null && !vers.equals(lastVers)) {
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
mActivityStarter.startActivityLocked(null, intent, null /*ephemeralIntent*/,
@@ -4678,15 +4690,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Intent intent, String resolvedType, IVoiceInteractionSession session,
IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions, int userId) {
- if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: startVoiceActivity() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.BIND_VOICE_INTERACTION;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
if (session == null || interactor == null) {
throw new NullPointerException("null session or interactor");
}
@@ -4701,15 +4705,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, Bundle bOptions, int userId) {
- if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
- != PackageManager.PERMISSION_GRANTED) {
- final String msg = "Permission Denial: startAssistantActivity() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + Manifest.permission.BIND_VOICE_INTERACTION;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ALLOW_FULL_ONLY, "startAssistantActivity", null);
return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
@@ -4718,6 +4714,49 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
+ public int startRecentsActivity(IAssistDataReceiver assistDataReceiver, Bundle bOptions,
+ int userId) {
+ if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
+ String msg = "Permission Denial: startRecentsActivity() from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " not recent tasks package";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ final int recentsUid = mRecentTasks.getRecentsComponentUid();
+ final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+ final String recentsPackage = recentsComponent.getPackageName();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ // If provided, kick off the request for the assist data in the background before
+ // starting the activity
+ if (assistDataReceiver != null) {
+ final AppOpsManager appOpsManager = (AppOpsManager)
+ mContext.getSystemService(Context.APP_OPS_SERVICE);
+ final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
+ assistDataReceiver, recentsPackage);
+ final AssistDataRequester requester = new AssistDataRequester(mContext, this,
+ mWindowManager, appOpsManager, proxy, this,
+ OP_ASSIST_STRUCTURE, OP_NONE);
+ requester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
+ true, false /* fetchScreenshots */, recentsUid, recentsPackage);
+ }
+
+ final Intent intent = new Intent();
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ intent.setComponent(recentsComponent);
+ return mActivityStarter.startActivityMayWait(null, recentsUid, recentsPackage,
+ intent, null, null, null, null, null, 0, 0, null, null, null, bOptions,
+ false, userId, null, "startRecentsActivity");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options)
throws RemoteException {
Slog.i(TAG, "Activity tried to startVoiceInteraction");
@@ -4863,7 +4902,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Intent.FLAG_ACTIVITY_FORWARD_RESULT|
Intent.FLAG_ACTIVITY_CLEAR_TOP|
Intent.FLAG_ACTIVITY_MULTIPLE_TASK|
- Intent.FLAG_ACTIVITY_NEW_TASK));
+ FLAG_ACTIVITY_NEW_TASK));
// Okay now we need to start the new activity, replacing the
// currently running activity. This is a little tricky because
@@ -6292,7 +6331,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mStackSupervisor.closeSystemDialogsLocked();
broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- AppOpsManager.OP_NONE, null, false, false,
+ OP_NONE, null, false, false,
-1, SYSTEM_UID, UserHandle.USER_ALL);
}
@@ -6394,7 +6433,7 @@ public class ActivityManagerService extends IActivityManager.Stub
intent.putExtra(Intent.EXTRA_UID, uid);
intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, null, 0, null, null, null, OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid));
}
@@ -10424,7 +10463,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+ " to windowingMode=" + windowingMode + " toTop=" + toTop);
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+ mWindowManager.setDockedStackCreateState(SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
null /* initialBounds */);
}
@@ -10471,7 +10510,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (stack.inSplitScreenPrimaryWindowingMode()) {
mWindowManager.setDockedStackCreateState(
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
}
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
@@ -10482,32 +10521,34 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
- * Moves the input task to the docked stack.
+ * Moves the input task to the primary-split-screen stack.
*
* @param taskId Id of task to move.
- * @param createMode The mode the docked stack should be created in if it doesn't exist
- * already. See
- * {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT}
+ * @param createMode The mode the primary split screen stack should be created in if it doesn't
+ * exist already. See
+ * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
* and
- * {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
+ * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
* @param toTop If the task and stack should be moved to the top.
* @param animate Whether we should play an animation for the moving the task
- * @param initialBounds If the docked stack gets created, it will use these bounds for the
- * docked stack. Pass {@code null} to use default bounds.
+ * @param initialBounds If the primary stack gets created, it will use these bounds for the
+ * stack. Pass {@code null} to use default bounds.
*/
@Override
- public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
- Rect initialBounds) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+ boolean animate, Rect initialBounds) {
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "setTaskWindowingModeSplitScreenPrimary()");
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
- Slog.w(TAG, "moveTaskToDockedStack: No task for id=" + taskId);
+ Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
return false;
}
- if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ if (DEBUG_STACK) Slog.d(TAG_STACK,
+ "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
@@ -10520,7 +10561,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// TODO: Should just change windowing mode vs. re-parenting...
final boolean moved = task.reparent(stack, toTop,
REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME,
- "moveTaskToDockedStack");
+ "setTaskWindowingModeSplitScreenPrimary");
if (moved) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
@@ -10806,7 +10847,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isAppPinning) {
+ private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
return;
@@ -10820,13 +10861,16 @@ public class ActivityManagerService extends IActivityManager.Stub
// When a task is locked, dismiss the pinned stack if it exists
mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
- // isAppPinning is used to distinguish between locked and pinned mode, as pinned mode
- // is initiated by system after the pinning request was shown and locked mode is initiated
- // by an authorized app directly
+ // {@code isSystemCaller} is used to distinguish whether this request is initiated by the
+ // system or a specific app.
+ // * System-initiated requests will only start the pinned mode (screen pinning)
+ // * App-initiated requests
+ // - will put the device in fully locked mode (LockTask), if the app is whitelisted
+ // - will start the pinned mode, otherwise
final int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
- mLockTaskController.startLockTaskMode(task, isAppPinning, callingUid);
+ mLockTaskController.startLockTaskMode(task, isSystemCaller, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10839,7 +10883,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (r == null) {
return;
}
- startLockTaskModeLocked(r.getTask(), false /* not system initiated */);
+ startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
}
}
@@ -10851,7 +10895,7 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
synchronized (this) {
startLockTaskModeLocked(mStackSupervisor.anyTaskForIdLocked(taskId),
- true /* system initiated */);
+ true /* isSystemCaller */);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10859,8 +10903,14 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void stopLockTaskMode() {
- stopLockTaskModeInternal(false /* not system initiated */);
+ public void stopLockTaskModeByToken(IBinder token) {
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ return;
+ }
+ stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
+ }
}
/**
@@ -10870,15 +10920,15 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void stopSystemLockTaskMode() throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
- stopLockTaskModeInternal(true /* system initiated */);
+ stopLockTaskModeInternal(null, true /* isSystemCaller */);
}
- private void stopLockTaskModeInternal(boolean isSystemRequest) {
+ private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) {
final int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- mLockTaskController.stopLockTaskMode(isSystemRequest, callingUid);
+ mLockTaskController.stopLockTaskMode(task, isSystemCaller, callingUid);
}
// Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
// task and jumping straight into a call in the case of emergency call back.
@@ -11629,7 +11679,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName);
@@ -12938,7 +12988,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean requestAssistContextExtras(int requestType, IResultReceiver receiver,
+ public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
@@ -12946,7 +12996,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean requestAutofillData(IResultReceiver receiver, Bundle receiverExtras,
+ public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
IBinder activityToken, int flags) {
return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
@@ -12954,7 +13004,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
- IResultReceiver receiver, Bundle receiverExtras, IBinder activityToken,
+ IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
int flags) {
enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
@@ -13022,7 +13072,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
- IResultReceiver receiver;
+ IAssistDataReceiver receiver;
synchronized (this) {
mPendingAssistExtras.remove(pae);
receiver = pae.receiver;
@@ -13031,10 +13081,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// Caller wants result sent back to them.
Bundle sendBundle = new Bundle();
// At least return the receiver extras
- sendBundle.putBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS,
- pae.receiverExtras);
+ sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
try {
- pae.receiver.send(0, sendBundle);
+ pae.receiver.onHandleAssistData(sendBundle);
} catch (RemoteException e) {
}
}
@@ -13072,7 +13121,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
// We are now ready to launch the assist activity.
- IResultReceiver sendReceiver = null;
+ IAssistDataReceiver sendReceiver = null;
Bundle sendBundle = null;
synchronized (this) {
buildAssistBundleLocked(pae, extras);
@@ -13085,16 +13134,15 @@ public class ActivityManagerService extends IActivityManager.Stub
if ((sendReceiver=pae.receiver) != null) {
// Caller wants result sent back to them.
sendBundle = new Bundle();
- sendBundle.putBundle(VoiceInteractionSession.KEY_DATA, pae.extras);
- sendBundle.putParcelable(VoiceInteractionSession.KEY_STRUCTURE, pae.structure);
- sendBundle.putParcelable(VoiceInteractionSession.KEY_CONTENT, pae.content);
- sendBundle.putBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS,
- pae.receiverExtras);
+ sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
+ sendBundle.putParcelable(ASSIST_KEY_STRUCTURE, pae.structure);
+ sendBundle.putParcelable(ASSIST_KEY_CONTENT, pae.content);
+ sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
}
}
if (sendReceiver != null) {
try {
- sendReceiver.send(0, sendBundle);
+ sendReceiver.onHandleAssistData(sendBundle);
} catch (RemoteException e) {
}
return;
@@ -13108,7 +13156,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mContext.startServiceAsUser(pae.intent, new UserHandle(pae.userHandle));
} else {
pae.intent.replaceExtras(pae.extras);
- pae.intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
closeSystemDialogs("assist");
@@ -13875,7 +13923,7 @@ public class ActivityManagerService extends IActivityManager.Stub
.setPackage("android")
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
broadcastIntent(null, intent, null, null, 0, null, null, null,
- android.app.AppOpsManager.OP_NONE, null, true, false, UserHandle.USER_ALL);
+ OP_NONE, null, true, false, UserHandle.USER_ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -14129,7 +14177,7 @@ public class ActivityManagerService extends IActivityManager.Stub
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, null, 0, null, null, null, OP_NONE,
null, false, false, MY_PID, SYSTEM_UID,
currentUserId);
intent = new Intent(Intent.ACTION_USER_STARTING);
@@ -14143,7 +14191,7 @@ public class ActivityManagerService extends IActivityManager.Stub
throws RemoteException {
}
}, 0, null, null,
- new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+ new String[] {INTERACT_ACROSS_USERS}, OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
} catch (Throwable t) {
Slog.wtf(TAG, "Failed sending first user broadcasts", t);
@@ -17043,22 +17091,39 @@ public class ActivityManagerService extends IActivityManager.Stub
return stringifySize(size * 1024, 1024);
}
- // Update this version number in case you change the 'compact' format
+ // Update this version number if you change the 'compact' format.
private static final int MEMINFO_COMPACT_VERSION = 1;
+ private static class MemoryUsageDumpOptions {
+ boolean dumpDetails;
+ boolean dumpFullDetails;
+ boolean dumpDalvik;
+ boolean dumpSummaryOnly;
+ boolean dumpUnreachable;
+ boolean oomOnly;
+ boolean isCompact;
+ boolean localOnly;
+ boolean packages;
+ boolean isCheckinRequest;
+ boolean dumpSwapPss;
+ boolean dumpProto;
+ }
+
final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) {
- boolean dumpDetails = false;
- boolean dumpFullDetails = false;
- boolean dumpDalvik = false;
- boolean dumpSummaryOnly = false;
- boolean dumpUnreachable = false;
- boolean oomOnly = false;
- boolean isCompact = false;
- boolean localOnly = false;
- boolean packages = false;
- boolean isCheckinRequest = false;
- boolean dumpSwapPss = false;
+ MemoryUsageDumpOptions opts = new MemoryUsageDumpOptions();
+ opts.dumpDetails = false;
+ opts.dumpFullDetails = false;
+ opts.dumpDalvik = false;
+ opts.dumpSummaryOnly = false;
+ opts.dumpUnreachable = false;
+ opts.oomOnly = false;
+ opts.isCompact = false;
+ opts.localOnly = false;
+ opts.packages = false;
+ opts.isCheckinRequest = false;
+ opts.dumpSwapPss = false;
+ opts.dumpProto = false;
int opti = 0;
while (opti < args.length) {
@@ -17068,29 +17133,31 @@ public class ActivityManagerService extends IActivityManager.Stub
}
opti++;
if ("-a".equals(opt)) {
- dumpDetails = true;
- dumpFullDetails = true;
- dumpDalvik = true;
- dumpSwapPss = true;
+ opts.dumpDetails = true;
+ opts.dumpFullDetails = true;
+ opts.dumpDalvik = true;
+ opts.dumpSwapPss = true;
} else if ("-d".equals(opt)) {
- dumpDalvik = true;
+ opts.dumpDalvik = true;
} else if ("-c".equals(opt)) {
- isCompact = true;
+ opts.isCompact = true;
} else if ("-s".equals(opt)) {
- dumpDetails = true;
- dumpSummaryOnly = true;
+ opts.dumpDetails = true;
+ opts.dumpSummaryOnly = true;
} else if ("-S".equals(opt)) {
- dumpSwapPss = true;
+ opts.dumpSwapPss = true;
} else if ("--unreachable".equals(opt)) {
- dumpUnreachable = true;
+ opts.dumpUnreachable = true;
} else if ("--oom".equals(opt)) {
- oomOnly = true;
+ opts.oomOnly = true;
} else if ("--local".equals(opt)) {
- localOnly = true;
+ opts.localOnly = true;
} else if ("--package".equals(opt)) {
- packages = true;
+ opts.packages = true;
} else if ("--checkin".equals(opt)) {
- isCheckinRequest = true;
+ opts.isCheckinRequest = true;
+ } else if ("--proto".equals(opt)) {
+ opts.dumpProto = true;
} else if ("-h".equals(opt)) {
pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]");
@@ -17104,6 +17171,7 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println(" --package: interpret process arg as package, dumping all");
pw.println(" processes that have loaded that package.");
pw.println(" --checkin: dump data for a checkin");
+ pw.println(" --proto: dump data to proto");
pw.println("If [process] is specified it can be the name or ");
pw.println("pid of a specific process to dump.");
return;
@@ -17112,21 +17180,33 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ String[] innerArgs = new String[args.length-opti];
+ System.arraycopy(args, opti, innerArgs, 0, args.length-opti);
+
+ ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, opts.packages, args);
+ if (opts.dumpProto) {
+ dumpApplicationMemoryUsage(fd, pw, opts, innerArgs, brief, procs);
+ } else {
+ dumpApplicationMemoryUsage(fd, pw, prefix, opts, innerArgs, brief, procs, categoryPw);
+ }
+ }
+
+ final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
+ MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
+ ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
final long[] tmpLong = new long[1];
- ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, packages, args);
if (procs == null) {
// No Java processes. Maybe they want to print a native process.
- if (args != null && args.length > opti
- && args[opti].charAt(0) != '-') {
+ if (innerArgs.length > 0 && innerArgs[0].charAt(0) != '-') {
ArrayList<ProcessCpuTracker.Stats> nativeProcs
= new ArrayList<ProcessCpuTracker.Stats>();
updateCpuStatsNow();
int findPid = -1;
try {
- findPid = Integer.parseInt(args[opti]);
+ findPid = Integer.parseInt(innerArgs[0]);
} catch (NumberFormatException e) {
}
synchronized (mProcessCpuTracker) {
@@ -17134,51 +17214,48 @@ public class ActivityManagerService extends IActivityManager.Stub
for (int i=0; i<N; i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
if (st.pid == findPid || (st.baseName != null
- && st.baseName.equals(args[opti]))) {
+ && st.baseName.equals(innerArgs[0]))) {
nativeProcs.add(st);
}
}
}
if (nativeProcs.size() > 0) {
- dumpApplicationMemoryUsageHeader(pw, uptime, realtime, isCheckinRequest,
- isCompact);
+ dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest,
+ opts.isCompact);
Debug.MemoryInfo mi = null;
for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
final ProcessCpuTracker.Stats r = nativeProcs.get(i);
final int pid = r.pid;
- if (!isCheckinRequest && dumpDetails) {
+ if (!opts.isCheckinRequest && opts.dumpDetails) {
pw.println("\n** MEMINFO in pid " + pid + " [" + r.baseName + "] **");
}
if (mi == null) {
mi = new Debug.MemoryInfo();
}
- if (dumpDetails || (!brief && !oomOnly)) {
+ if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
Debug.getMemoryInfo(pid, mi);
} else {
mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
mi.dalvikPrivateDirty = (int)tmpLong[0];
}
- ActivityThread.dumpMemInfoTable(pw, mi, isCheckinRequest, dumpFullDetails,
- dumpDalvik, dumpSummaryOnly, pid, r.baseName, 0, 0, 0, 0, 0, 0);
- if (isCheckinRequest) {
+ ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails,
+ opts.dumpDalvik, opts.dumpSummaryOnly, pid, r.baseName, 0, 0, 0, 0, 0, 0);
+ if (opts.isCheckinRequest) {
pw.println();
}
}
return;
}
}
- pw.println("No process found for: " + args[opti]);
+ pw.println("No process found for: " + innerArgs[0]);
return;
}
- if (!brief && !oomOnly && (procs.size() == 1 || isCheckinRequest || packages)) {
- dumpDetails = true;
+ if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
+ opts.dumpDetails = true;
}
- dumpApplicationMemoryUsageHeader(pw, uptime, realtime, isCheckinRequest, isCompact);
-
- String[] innerArgs = new String[args.length-opti];
- System.arraycopy(args, opti, innerArgs, 0, args.length-opti);
+ dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact);
ArrayList<MemItem> procMems = new ArrayList<MemItem>();
final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
@@ -17186,9 +17263,9 @@ public class ActivityManagerService extends IActivityManager.Stub
long nativeSwapPss = 0;
long dalvikPss = 0;
long dalvikSwapPss = 0;
- long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+ long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
- long[] dalvikSubitemSwapPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+ long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
long otherPss = 0;
long otherSwapPss = 0;
@@ -17220,24 +17297,24 @@ public class ActivityManagerService extends IActivityManager.Stub
hasActivities = r.activities.size() > 0;
}
if (thread != null) {
- if (!isCheckinRequest && dumpDetails) {
+ if (!opts.isCheckinRequest && opts.dumpDetails) {
pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
}
if (mi == null) {
mi = new Debug.MemoryInfo();
}
- if (dumpDetails || (!brief && !oomOnly)) {
+ if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
Debug.getMemoryInfo(pid, mi);
hasSwapPss = mi.hasSwappedOutPss;
} else {
mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
mi.dalvikPrivateDirty = (int)tmpLong[0];
}
- if (dumpDetails) {
- if (localOnly) {
- ActivityThread.dumpMemInfoTable(pw, mi, isCheckinRequest, dumpFullDetails,
- dumpDalvik, dumpSummaryOnly, pid, r.processName, 0, 0, 0, 0, 0, 0);
- if (isCheckinRequest) {
+ if (opts.dumpDetails) {
+ if (opts.localOnly) {
+ ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails,
+ opts.dumpDalvik, opts.dumpSummaryOnly, pid, r.processName, 0, 0, 0, 0, 0, 0);
+ if (opts.isCheckinRequest) {
pw.println();
}
} else {
@@ -17246,19 +17323,19 @@ public class ActivityManagerService extends IActivityManager.Stub
TransferPipe tp = new TransferPipe();
try {
thread.dumpMemInfo(tp.getWriteFd(),
- mi, isCheckinRequest, dumpFullDetails,
- dumpDalvik, dumpSummaryOnly, dumpUnreachable, innerArgs);
+ mi, opts.isCheckinRequest, opts.dumpFullDetails,
+ opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, innerArgs);
tp.go(fd);
} finally {
tp.kill();
}
} catch (IOException e) {
- if (!isCheckinRequest) {
+ if (!opts.isCheckinRequest) {
pw.println("Got IoException! " + e);
pw.flush();
}
} catch (RemoteException e) {
- if (!isCheckinRequest) {
+ if (!opts.isCheckinRequest) {
pw.println("Got RemoteException! " + e);
pw.flush();
}
@@ -17277,7 +17354,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- if (!isCheckinRequest && mi != null) {
+ if (!opts.isCheckinRequest && mi != null) {
totalPss += myTotalPss;
totalSwapPss += myTotalSwapPss;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
@@ -17330,7 +17407,7 @@ public class ActivityManagerService extends IActivityManager.Stub
long nativeProcTotalPss = 0;
- if (!isCheckinRequest && procs.size() > 1 && !packages) {
+ if (!opts.isCheckinRequest && procs.size() > 1 && !opts.packages) {
// If we are showing aggregations, also look for native processes to
// include so that our aggregations are more accurate.
updateCpuStatsNow();
@@ -17343,7 +17420,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (mi == null) {
mi = new Debug.MemoryInfo();
}
- if (!brief && !oomOnly) {
+ if (!brief && !opts.oomOnly) {
Debug.getMemoryInfo(st.pid, mi);
} else {
mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null);
@@ -17430,7 +17507,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
for (int j=0; j<oomPss.length; j++) {
if (oomPss[j] != 0) {
- String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
+ String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
: DUMP_MEM_OOM_LABEL[j];
MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j],
DUMP_MEM_OOM_ADJ[j]);
@@ -17439,26 +17516,26 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- dumpSwapPss = dumpSwapPss && hasSwapPss && totalSwapPss != 0;
- if (!brief && !oomOnly && !isCompact) {
+ opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
+ if (!brief && !opts.oomOnly && !opts.isCompact) {
pw.println();
pw.println("Total PSS by process:");
- dumpMemItems(pw, " ", "proc", procMems, true, isCompact, dumpSwapPss);
+ dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, opts.dumpSwapPss);
pw.println();
}
- if (!isCompact) {
+ if (!opts.isCompact) {
pw.println("Total PSS by OOM adjustment:");
}
- dumpMemItems(pw, " ", "oom", oomMems, false, isCompact, dumpSwapPss);
- if (!brief && !oomOnly) {
+ dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, opts.dumpSwapPss);
+ if (!brief && !opts.oomOnly) {
PrintWriter out = categoryPw != null ? categoryPw : pw;
- if (!isCompact) {
+ if (!opts.isCompact) {
out.println();
out.println("Total PSS by category:");
}
- dumpMemItems(out, " ", "cat", catMems, true, isCompact, dumpSwapPss);
+ dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, opts.dumpSwapPss);
}
- if (!isCompact) {
+ if (!opts.isCompact) {
pw.println();
}
MemInfoReader memInfo = new MemInfoReader();
@@ -17476,7 +17553,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
if (!brief) {
- if (!isCompact) {
+ if (!opts.isCompact) {
pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
pw.print(" (status ");
switch (mLastMemoryLevel) {
@@ -17517,7 +17594,7 @@ public class ActivityManagerService extends IActivityManager.Stub
long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
- if (!isCompact) {
+ if (!opts.isCompact) {
pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
+ memInfo.getKernelUsedSizeKb())); pw.print(" (");
pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
@@ -17528,7 +17605,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (!brief) {
if (memInfo.getZramTotalSizeKb() != 0) {
- if (!isCompact) {
+ if (!opts.isCompact) {
pw.print(" ZRAM: ");
pw.print(stringifyKBSize(memInfo.getZramTotalSizeKb()));
pw.print(" physical used for ");
@@ -17544,7 +17621,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
final long[] ksm = getKsmInfo();
- if (!isCompact) {
+ if (!opts.isCompact) {
if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0
|| ksm[KSM_VOLATILE] != 0) {
pw.print(" KSM: "); pw.print(stringifyKBSize(ksm[KSM_SHARING]));
@@ -17593,6 +17670,17 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw,
+ MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
+ ArrayList<ProcessRecord> procs) {
+ ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ // TODO: implement
+ pw.println("Not yet implemented. Have a cookie instead! :]");
+
+ proto.flush();
+ }
+
private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss,
long memtrack, String name) {
sb.append(" ");
@@ -18816,7 +18904,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
- null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers,
+ null, -1, -1, false, null, null, OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
@@ -19812,7 +19900,7 @@ public class ActivityManagerService extends IActivityManager.Stub
: new String[] {requiredPermission};
int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
resultTo, resultCode, resultData, resultExtras,
- requiredPermissions, AppOpsManager.OP_NONE, bOptions, serialized,
+ requiredPermissions, OP_NONE, bOptions, serialized,
sticky, -1, uid, userId);
Binder.restoreCallingIdentity(origId);
return res;
@@ -20435,7 +20523,7 @@ public class ActivityManagerService extends IActivityManager.Stub
| Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
UserHandle.USER_ALL);
if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
@@ -20446,7 +20534,7 @@ public class ActivityManagerService extends IActivityManager.Stub
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
UserHandle.USER_ALL);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index f9422656c7db..7eb922c9cd80 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -373,7 +373,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
if (mProfileFile != null || mAgent != null) {
ParcelFileDescriptor fd = null;
if (mProfileFile != null) {
- fd = openOutputFileForSystem(mProfileFile);
+ fd = openFileForSystem(mProfileFile, "w");
if (fd == null) {
return 1;
}
@@ -668,7 +668,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
File file = new File(filename);
file.delete();
- ParcelFileDescriptor fd = openOutputFileForSystem(filename);
+ ParcelFileDescriptor fd = openFileForSystem(filename, "w");
if (fd == null) {
return -1;
}
@@ -756,7 +756,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
if (start) {
profileFile = getNextArgRequired();
- fd = openOutputFileForSystem(profileFile);
+ fd = openFileForSystem(profileFile, "w");
if (fd == null) {
return -1;
}
@@ -820,7 +820,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
File file = new File(heapFile);
file.delete();
- ParcelFileDescriptor fd = openOutputFileForSystem(heapFile);
+ ParcelFileDescriptor fd = openFileForSystem(heapFile, "w");
if (fd == null) {
return -1;
}
@@ -2476,7 +2476,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a");
pw.println(" common form is [-e <testrunner_flag> <value>[,<value>...]].");
pw.println(" -p <FILE>: write profiling data to <FILE>");
- pw.println(" -m: Write output as protobuf (machine readable)");
+ pw.println(" -m: Write output as protobuf to stdout (machine readable)");
+ pw.println(" -f <Optional PATH/TO/FILE>: Write output as protobuf to a file (machine");
+ pw.println(" readable). If path is not specified, default directory and file name will");
+ pw.println(" be used: /sdcard/instrument-logs/log-yyyyMMdd-hhmmss-SSS.instrumentation_data_proto");
pw.println(" -w: wait for instrumentation to finish before returning. Required for");
pw.println(" test runners.");
pw.println(" --user <USER_ID> | current: Specify user instrumentation runs in;");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 2f0b64918bf9..481575e88c70 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -77,7 +77,6 @@ import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.os.Build.VERSION_CODES.O;
-import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
@@ -886,6 +885,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
Entry ent = AttributeCache.instance().get(packageName,
realTheme, com.android.internal.R.styleable.Window, userId);
+
if (ent != null) {
fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
@@ -2119,11 +2119,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
void setRequestedOrientation(int requestedOrientation) {
- if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen
- && appInfo.targetSdkVersion >= O_MR1) {
- throw new IllegalStateException("Only fullscreen activities can request orientation");
- }
-
final int displayId = getDisplayId();
final Configuration displayConfig =
mStackSupervisor.getDisplayOverrideConfiguration(displayId);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ba41bd4c3b6f..88403fc5dde7 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3474,7 +3474,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
if (endTask) {
- mService.mLockTaskController.removeLockedTask(task);
+ mService.mLockTaskController.clearLockedTask(task);
}
} else if (r.state != ActivityState.PAUSING) {
// If the activity is PAUSING, we will complete the finish once
@@ -4334,8 +4334,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
Slog.i(TAG, "moveTaskToBack: " + tr);
- // If the task is locked, then show the lock task toast
- if (mService.mLockTaskController.checkLockedTask(tr)) {
+ // In LockTask mode, moving a locked task to the back of the stack may expose unlocked
+ // ones. Therefore we need to check if this operation is allowed.
+ if (!mService.mLockTaskController.canMoveTaskToBack(tr)) {
return false;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6ec158efa1cd..43a2a9f1f9bd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.ACTIVITY_EMBEDDING;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
@@ -83,6 +84,7 @@ import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
@@ -1306,8 +1308,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mService.updateLruProcessLocked(app, true, null);
mService.updateOomAdjLocked();
- if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE ||
- task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) {
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
+ || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
+ || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
+ && mService.mLockTaskController.getLockTaskModeState()
+ == LOCK_TASK_MODE_LOCKED)) {
mService.mLockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
}
@@ -2769,6 +2774,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (tr != null) {
tr.removeTaskActivitiesLocked(pauseImmediately);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
+ mService.mLockTaskController.clearLockedTask(tr);
if (tr.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
@@ -4465,7 +4471,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
try {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mWindowManager.setDockedStackCreateState(
- activityOptions.getDockCreateMode(), null /* initialBounds */);
+ activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
// Defer updating the stack in which recents is until the app transition is done, to
// not run into issues where we still need to draw the task in recents but the
diff --git a/services/core/java/com/android/server/am/AssistDataReceiverProxy.java b/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
new file mode 100644
index 000000000000..8306731b7635
--- /dev/null
+++ b/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
@@ -0,0 +1,87 @@
+/*
+ * 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 static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.graphics.Bitmap;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.app.IAssistDataReceiver;
+import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
+
+/**
+ * Proxies assist data to the given receiver, skipping all callbacks if the receiver dies.
+ */
+class AssistDataReceiverProxy implements AssistDataRequesterCallbacks,
+ Binder.DeathRecipient {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "AssistDataReceiverProxy" : TAG_AM;
+
+ private String mCallerPackage;
+ private boolean mBinderDied;
+ private IAssistDataReceiver mReceiver;
+
+ public AssistDataReceiverProxy(IAssistDataReceiver receiver, String callerPackage) {
+ try {
+ receiver.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not link to client death", e);
+ }
+ mReceiver = receiver;
+ mCallerPackage = callerPackage;
+ }
+
+ @Override
+ public boolean canHandleReceivedAssistDataLocked() {
+ // We are forwarding, so we can always receive this data
+ return true;
+ }
+
+ @Override
+ public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+ if (!mBinderDied) {
+ try {
+ mReceiver.onHandleAssistData(data);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to proxy assist data to receiver in package="
+ + mCallerPackage, e);
+ }
+ }
+ }
+
+ @Override
+ public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+ if (!mBinderDied) {
+ try {
+ mReceiver.onHandleAssistScreenshot(screenshot);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to proxy assist screenshot to receiver in package="
+ + mCallerPackage, e);
+ }
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mBinderDied = true;
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
new file mode 100644
index 000000000000..e32ff6e9cb0b
--- /dev/null
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -0,0 +1,307 @@
+/*
+ * 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 static android.app.ActivityManager.ASSIST_CONTEXT_FULL;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_NONE;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.IWindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAssistDataReceiver;
+import com.android.internal.logging.MetricsLogger;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to asynchronously fetch the assist data and screenshot from the current running
+ * activities. It manages received data and calls back to the owner when the owner is ready to
+ * receive the data itself.
+ */
+public class AssistDataRequester extends IAssistDataReceiver.Stub {
+
+ public static final String KEY_RECEIVER_EXTRA_COUNT = "count";
+ public static final String KEY_RECEIVER_EXTRA_INDEX = "index";
+
+ private IActivityManager mService;
+ private IWindowManager mWindowManager;
+ private Context mContext;
+ private AppOpsManager mAppOpsManager;
+
+ private AssistDataRequesterCallbacks mCallbacks;
+ private Object mCallbacksLock;
+
+ private int mRequestStructureAppOps;
+ private int mRequestScreenshotAppOps;
+ private boolean mCanceled;
+ private int mPendingDataCount;
+ private int mPendingScreenshotCount;
+ private final ArrayList<Bundle> mAssistData = new ArrayList<>();
+ private final ArrayList<Bitmap> mAssistScreenshot = new ArrayList<>();
+
+
+ /**
+ * Interface to handle the events from the fetcher.
+ */
+ public interface AssistDataRequesterCallbacks {
+ /**
+ * @return whether the currently received assist data can be handled by the callbacks.
+ */
+ @GuardedBy("mCallbacksLock")
+ boolean canHandleReceivedAssistDataLocked();
+
+ /**
+ * Called when we receive asynchronous assist data. This call is only made if the
+ * {@param fetchData} argument to requestAssistData() is true, and if the current activity
+ * allows assist data to be fetched. In addition, the callback will be made with the
+ * {@param mCallbacksLock} held, and only if {@link #canHandleReceivedAssistDataLocked()}
+ * is true.
+ */
+ @GuardedBy("mCallbacksLock")
+ void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount);
+
+ /**
+ * Called when we receive asynchronous assist screenshot. This call is only made if
+ * {@param fetchScreenshot} argument to requestAssistData() is true, and if the current
+ * activity allows assist data to be fetched. In addition, the callback will be made with
+ * the {@param mCallbacksLock} held, and only if
+ * {@link #canHandleReceivedAssistDataLocked()} is true.
+ */
+ @GuardedBy("mCallbacksLock")
+ void onAssistScreenshotReceivedLocked(Bitmap screenshot);
+ }
+
+ /**
+ * @param callbacks The callbacks to handle the asynchronous reply with the assist data.
+ * @param callbacksLock The lock for the requester to hold when calling any of the
+ * {@param callbacks}. The owner should also take care in locking
+ * appropriately when calling into this requester.
+ * @param requestStructureAppOps The app ops to check before requesting the assist structure
+ * @param requestScreenshotAppOps The app ops to check before requesting the assist screenshot.
+ * This can be {@link AppOpsManager#OP_NONE} to indicate that
+ * screenshots should never be fetched.
+ */
+ public AssistDataRequester(Context context, IActivityManager service,
+ IWindowManager windowManager, AppOpsManager appOpsManager,
+ AssistDataRequesterCallbacks callbacks, Object callbacksLock,
+ int requestStructureAppOps, int requestScreenshotAppOps) {
+ mCallbacks = callbacks;
+ mCallbacksLock = callbacksLock;
+ mWindowManager = windowManager;
+ mService = service;
+ mContext = context;
+ mAppOpsManager = appOpsManager;
+ mRequestStructureAppOps = requestStructureAppOps;
+ mRequestScreenshotAppOps = requestScreenshotAppOps;
+ }
+
+ /**
+ * Request that assist data be loaded asynchronously. The resulting data will be provided
+ * through the {@link AssistDataRequesterCallbacks}.
+ *
+ * @param activityTokens the list of visible activities
+ * @param fetchData whether or not to fetch the assist data, only applies if the caller is
+ * allowed to fetch the assist data, and the current activity allows assist data to be
+ * fetched from it
+ * @param fetchScreenshot whether or not to fetch the screenshot, only applies if fetchData is
+ * true, the caller is allowed to fetch the assist data, and the current activity allows
+ * assist data to be fetched from it
+ */
+ public void requestAssistData(List<IBinder> activityTokens, boolean fetchData,
+ boolean fetchScreenshot, int callingUid, String callingPackage) {
+ // TODO: Better handle the cancel case if a request can be reused
+ // TODO: Known issue, if the assist data is not allowed on the current activity, then no
+ // assist data is requested for any of the other activities
+
+ // Early exit if there are no activity to fetch for
+ if (activityTokens.isEmpty()) {
+ return;
+ }
+
+ // Ensure that the current activity supports assist data
+ boolean isAssistDataAllowed = false;
+ try {
+ isAssistDataAllowed = mService.isAssistDataAllowedOnCurrentActivity();
+ } catch (RemoteException e) {
+ // Should never happen
+ }
+ fetchData &= isAssistDataAllowed;
+ fetchScreenshot &= fetchData && isAssistDataAllowed
+ && (mRequestScreenshotAppOps != OP_NONE);
+
+ mCanceled = false;
+ mPendingDataCount = 0;
+ mPendingScreenshotCount = 0;
+ mAssistData.clear();
+ mAssistScreenshot.clear();
+
+ if (fetchData) {
+ if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
+ == MODE_ALLOWED) {
+ final int numActivities = activityTokens.size();
+ for (int i = 0; i < numActivities; i++) {
+ IBinder topActivity = activityTokens.get(i);
+ try {
+ MetricsLogger.count(mContext, "assist_with_context", 1);
+ Bundle receiverExtras = new Bundle();
+ receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
+ receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
+ if (mService.requestAssistContextExtras(ASSIST_CONTEXT_FULL, this,
+ receiverExtras, topActivity, /* focused= */ i == 0,
+ /* newSessionId= */ i == 0)) {
+ mPendingDataCount++;
+ } else if (i == 0) {
+ // Wasn't allowed... given that, let's not do the screenshot either.
+ dispatchAssistDataReceived(null);
+ fetchScreenshot = false;
+ break;
+ }
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ }
+ } else {
+ // Wasn't allowed... given that, let's not do the screenshot either.
+ dispatchAssistDataReceived(null);
+ fetchScreenshot = false;
+ }
+ }
+
+ if (fetchScreenshot) {
+ if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
+ == MODE_ALLOWED) {
+ try {
+ MetricsLogger.count(mContext, "assist_with_screen", 1);
+ mPendingScreenshotCount++;
+ mWindowManager.requestAssistScreenshot(this);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ } else {
+ dispatchAssistScreenshotReceived(null);
+ }
+ }
+ }
+
+ /**
+ * This call should only be made when the callbacks are capable of handling the received assist
+ * data.
+ */
+ public void processPendingAssistData() {
+ final int dataCount = mAssistData.size();
+ for (int i = 0; i < dataCount; i++) {
+ dispatchAssistDataReceived(mAssistData.get(i));
+ }
+ final int screenshotsCount = mAssistScreenshot.size();
+ for (int i = 0; i < screenshotsCount; i++) {
+ dispatchAssistScreenshotReceived(mAssistScreenshot.get(i));
+ }
+ }
+
+ public int getPendingDataCount() {
+ return mPendingDataCount;
+ }
+
+ public int getPendingScreenshotCount() {
+ return mPendingScreenshotCount;
+ }
+
+ /**
+ * Cancels the current request for the assist data.
+ */
+ public void cancel() {
+ // Reset the pending data count, if we receive new assist data after this point, it will
+ // be ignored
+ mCanceled = true;
+ mPendingDataCount = 0;
+ mPendingScreenshotCount = 0;
+ mAssistData.clear();
+ mAssistScreenshot.clear();
+ }
+
+ @Override
+ public void onHandleAssistData(Bundle data) {
+ synchronized (mCallbacksLock) {
+ if (mCanceled) {
+ return;
+ }
+ mPendingDataCount--;
+
+ if (mCallbacks.canHandleReceivedAssistDataLocked()) {
+ // Process any pending data and dispatch the new data as well
+ processPendingAssistData();
+ dispatchAssistDataReceived(data);
+ } else {
+ // Queue up the data for processing later
+ mAssistData.add(data);
+ }
+ }
+ }
+
+ @Override
+ public void onHandleAssistScreenshot(Bitmap screenshot) {
+ synchronized (mCallbacksLock) {
+ if (mCanceled) {
+ return;
+ }
+ mPendingScreenshotCount--;
+
+ if (mCallbacks.canHandleReceivedAssistDataLocked()) {
+ // Process any pending data and dispatch the new data as well
+ processPendingAssistData();
+ dispatchAssistScreenshotReceived(screenshot);
+ } else {
+ // Queue up the data for processing later
+ mAssistScreenshot.add(screenshot);
+ }
+ }
+ }
+
+ private void dispatchAssistDataReceived(Bundle data) {
+ int activityIndex = 0;
+ int activityCount = 0;
+ final Bundle receiverExtras = data != null
+ ? data.getBundle(ASSIST_KEY_RECEIVER_EXTRAS) : null;
+ if (receiverExtras != null) {
+ activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX);
+ activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT);
+ }
+ mCallbacks.onAssistDataReceivedLocked(data, activityIndex, activityCount);
+ }
+
+ private void dispatchAssistScreenshotReceived(Bitmap screenshot) {
+ mCallbacks.onAssistScreenshotReceivedLocked(screenshot);
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mPendingDataCount="); pw.println(mPendingDataCount);
+ pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
+ pw.print(prefix); pw.print("mPendingScreenshotCount="); pw.println(mPendingScreenshotCount);
+ pw.print(prefix); pw.print("mAssistScreenshot="); pw.println(mAssistScreenshot);
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index db12ae252abc..52f97020b5a7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@ import android.content.pm.PackageManager;
import android.net.wifi.WifiActivityEnergyInfo;
import android.os.PowerSaveState;
import android.os.BatteryStats;
+import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -177,9 +178,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
public void publish() {
+ LocalServices.addService(BatteryStatsInternal.class, new LocalService());
ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
}
+ private final class LocalService extends BatteryStatsInternal {
+ @Override
+ public String[] getWifiIfaces() {
+ return mStats.getWifiIfaces().clone();
+ }
+
+ @Override
+ public String[] getMobileIfaces() {
+ return mStats.getMobileIfaces().clone();
+ }
+ }
+
private static void awaitUninterruptibly(Future<?> future) {
while (true) {
try {
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 4b2a08439f2d..e87b4e63c5b0 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -143,9 +143,17 @@ public class LockTaskController {
LockTaskNotify mLockTaskNotify;
/**
- * The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
- * may be finished until there is only one entry left. If this is empty the system is not
- * in lockTask mode.
+ * The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode.
+ *
+ * The first task in the list, which started the current LockTask session, is called the root
+ * task. It coincides with the Home task in a typical multi-app kiosk deployment. When there are
+ * more than one locked tasks, the root task can't be finished. Nor can it be moved to the back
+ * of the stack by {@link ActivityStack#moveTaskToBackLocked(int)};
+ *
+ * Calling {@link Activity#stopLockTask()} on the root task will finish all tasks but itself in
+ * this list, and the device will exit LockTask mode.
+ *
+ * The list is empty if LockTask is inactive.
*/
private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
@@ -164,7 +172,7 @@ public class LockTaskController {
* {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
* {@link ActivityManager#LOCK_TASK_MODE_PINNED}
*/
- private int mLockTaskModeState;
+ private int mLockTaskModeState = LOCK_TASK_MODE_NONE;
/**
* This is ActivityStackSupervisor's Handler.
@@ -199,24 +207,28 @@ public class LockTaskController {
* @return whether the given task is locked at the moment. Locked tasks cannot be moved to the
* back of the stack.
*/
- boolean checkLockedTask(TaskRecord task) {
- if (mLockTaskModeTasks.contains(task)) {
- showLockTaskToast();
- return true;
- }
- return false;
+ @VisibleForTesting
+ boolean isTaskLocked(TaskRecord task) {
+ return mLockTaskModeTasks.contains(task);
}
/**
- * @return whether the given activity is blocked from finishing, because it is the root activity
+ * @return {@code true} whether this task first started the current LockTask session.
+ */
+ private boolean isRootTask(TaskRecord task) {
+ return mLockTaskModeTasks.indexOf(task) == 0;
+ }
+
+ /**
+ * @return whether the given activity is blocked from finishing, because it is the only activity
* of the last locked task and finishing it would mean that lock task mode is ended illegally.
*/
boolean activityBlockedFromFinish(ActivityRecord activity) {
- TaskRecord task = activity.getTask();
+ final TaskRecord task = activity.getTask();
if (activity == task.getRootActivity()
+ && activity == task.getTopActivity()
&& task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
- && mLockTaskModeTasks.size() == 1
- && mLockTaskModeTasks.contains(task)) {
+ && isRootTask(task)) {
Slog.i(TAG, "Not finishing task in lock task mode");
showLockTaskToast();
return true;
@@ -225,6 +237,19 @@ public class LockTaskController {
}
/**
+ * @return whether the given task can be moved to the back of the stack with
+ * {@link ActivityStack#moveTaskToBackLocked(int)}
+ * @see #mLockTaskModeTasks
+ */
+ boolean canMoveTaskToBack(TaskRecord task) {
+ if (isRootTask(task)) {
+ showLockTaskToast();
+ return false;
+ }
+ return true;
+ }
+
+ /**
* @return whether the requested task is allowed to be launched.
*/
boolean isLockTaskModeViolation(TaskRecord task) {
@@ -246,7 +271,7 @@ public class LockTaskController {
private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
// TODO: Double check what's going on here. If the task is already in lock task mode, it's
// likely whitelisted, so will return false below.
- if (getLockedTask() == task && !isNewClearTask) {
+ if (isTaskLocked(task) && !isNewClearTask) {
// If the task is already at the top and won't be cleared, then allow the operation
return false;
}
@@ -270,80 +295,116 @@ public class LockTaskController {
/**
* Stop the current lock task mode.
*
- * @param isSystemInitiated indicates whether this request was initiated by the system via
- * {@link ActivityManagerService#stopSystemLockTaskMode()}.
+ * This is called by {@link ActivityManagerService} and performs various checks before actually
+ * finishing the locked task.
+ *
+ * @param task the task that requested the end of lock task mode ({@code null} for quitting app
+ * pinning mode)
+ * @param isSystemCaller indicates whether this request comes from the system via
+ * {@link ActivityManagerService#stopSystemLockTaskMode()}. If
+ * {@code true}, it means the user intends to stop pinned mode through UI;
+ * otherwise, it's called by an app and we need to stop locked or pinned
+ * mode, subject to checks.
* @param callingUid the caller that requested the end of lock task mode.
+ * @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in
+ * foreground)
* @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
* they differ from the one that launched lock task mode.
*/
- void stopLockTaskMode(boolean isSystemInitiated, int callingUid) {
- final TaskRecord lockTask = getLockedTask();
- if (lockTask == null || mLockTaskModeState == LOCK_TASK_MODE_NONE) {
- // Our work here is done.
+ void stopLockTaskMode(@Nullable TaskRecord task, boolean isSystemCaller, int callingUid) {
+ if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
return;
}
- if (isSystemInitiated && mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
- // As system can only start app pinning, we also only let it unlock in this mode.
- showLockTaskToast();
- return;
+ if (isSystemCaller) {
+ if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+ clearLockedTasks("stopAppPinning");
+ } else {
+ Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true");
+ showLockTaskToast();
+ }
+
+ } else {
+ // Ensure calling activity is not null
+ if (task == null) {
+ throw new IllegalArgumentException("can't stop LockTask for null task");
+ }
+
+ // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
+ // It is possible lockTaskMode was started by the system process because
+ // android:lockTaskMode is set to a locking value in the application manifest
+ // instead of the app calling startLockTaskMode. In this case
+ // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the
+ // {@link TaskRecord.effectiveUid} instead. Also caller with
+ // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task.
+ if (callingUid != task.mLockTaskUid
+ && (task.mLockTaskUid != 0 || callingUid != task.effectiveUid)) {
+ throw new SecurityException("Invalid uid, expected " + task.mLockTaskUid
+ + " callingUid=" + callingUid + " effectiveUid=" + task.effectiveUid);
+ }
+
+ // We don't care if it's pinned or locked mode; this will stop it anyways.
+ clearLockedTask(task);
}
+ }
- // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
- // It is possible lockTaskMode was started by the system process because
- // android:lockTaskMode is set to a locking value in the application manifest
- // instead of the app calling startLockTaskMode. In this case
- // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the
- // {@link TaskRecord.effectiveUid} instead. Also caller with
- // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task.
- if (!isSystemInitiated && callingUid != lockTask.mLockTaskUid
- && (lockTask.mLockTaskUid != 0 || callingUid != lockTask.effectiveUid)) {
- throw new SecurityException("Invalid uid, expected " + lockTask.mLockTaskUid
- + " callingUid=" + callingUid + " effectiveUid=" + lockTask.effectiveUid);
+ /**
+ * Clear all locked tasks and request the end of LockTask mode.
+ *
+ * This method is called by {@link UserController} when starting a new foreground user, and,
+ * unlike {@link #stopLockTaskMode(TaskRecord, boolean, int)}, it doesn't perform the checks.
+ */
+ void clearLockedTasks(String reason) {
+ if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason);
+ if (!mLockTaskModeTasks.isEmpty()) {
+ clearLockedTask(mLockTaskModeTasks.get(0));
+ }
+ }
+
+ /**
+ * Clear one locked task from LockTask mode.
+ *
+ * If the requested task is the root task (see {@link #mLockTaskModeTasks}), then all locked
+ * tasks are cleared. Otherwise, only the requested task is cleared. LockTask mode is stopped
+ * when the last locked task is cleared.
+ *
+ * @param task the task to be cleared from LockTask mode.
+ */
+ void clearLockedTask(final TaskRecord task) {
+ if (task == null || mLockTaskModeTasks.isEmpty()) return;
+
+ if (task == mLockTaskModeTasks.get(0)) {
+ // We're removing the root task while there are other locked tasks. Therefore we should
+ // clear all locked tasks in reverse order.
+ for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx > 0; --taskNdx) {
+ clearLockedTask(mLockTaskModeTasks.get(taskNdx));
+ }
}
- clearLockTaskMode("stopLockTask");
+ removeLockedTask(task);
+ if (mLockTaskModeTasks.isEmpty()) {
+ return;
+ }
+ task.performClearTaskLocked();
+ mSupervisor.resumeFocusedStackTopActivityLocked();
}
/**
* Remove the given task from the locked task list. If this was the last task in the list,
* lock task mode is stopped.
*/
- void removeLockedTask(final TaskRecord task) {
+ private void removeLockedTask(final TaskRecord task) {
if (!mLockTaskModeTasks.remove(task)) {
return;
}
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "removeLockedTask: removed " + task);
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task);
if (mLockTaskModeTasks.isEmpty()) {
- // Last one.
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
" last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
mHandler.post(() -> performStopLockTask(task.userId));
}
}
- /**
- * Remove the topmost task from the locked task list. If this is the last task in the list, it
- * will result in the end of locked task mode.
- */
- void clearLockTaskMode(String reason) {
- // Take out of lock task mode if necessary
- final TaskRecord lockedTask = getLockedTask();
- if (lockedTask != null) {
- removeLockedTask(lockedTask);
- if (!mLockTaskModeTasks.isEmpty()) {
- // There are locked tasks remaining, can only finish this task, not unlock it.
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
- "setLockTaskMode: Tasks remaining, can't unlock");
- lockedTask.performClearTaskLocked();
- mSupervisor.resumeFocusedStackTopActivityLocked();
- return;
- }
- }
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
- "setLockTaskMode: No tasks to unlock. Callers=" + Debug.getCallers(4));
- }
-
// This method should only be called on the handler thread
private void performStopLockTask(int userId) {
// When lock task ends, we enable the status bars.
@@ -382,17 +443,18 @@ public class LockTaskController {
* Method to start lock task mode on a given task.
*
* @param task the task that should be locked.
- * @param isSystemInitiated indicates whether this request was initiated by the system via
- * {@link ActivityManagerService#startSystemLockTaskMode(int)}.
+ * @param isSystemCaller indicates whether this request was initiated by the system via
+ * {@link ActivityManagerService#startSystemLockTaskMode(int)}. If
+ * {@code true}, this intends to start pinned mode; otherwise, we look
+ * at the calling task's mLockTaskAuth to decide which mode to start.
* @param callingUid the caller that requested the launch of lock task mode.
*/
- void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemInitiated,
- int callingUid) {
- if (!isSystemInitiated) {
+ void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemCaller, int callingUid) {
+ if (!isSystemCaller) {
task.mLockTaskUid = callingUid;
if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
// startLockTask() called by app, but app is not part of lock task whitelist. Show
- // app pinning request. We will come back here with isSystemInitiated true.
+ // app pinning request. We will come back here with isSystemCaller true.
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
StatusBarManagerInternal statusBarManager = LocalServices.getService(
StatusBarManagerInternal.class);
@@ -404,8 +466,9 @@ public class LockTaskController {
}
// System can only initiate screen pinning, not full lock task mode
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, isSystemInitiated ? "Locking pinned" : "Locking fully");
- setLockTaskMode(task, isSystemInitiated ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+ isSystemCaller ? "Locking pinned" : "Locking fully");
+ setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
"startLockTask", true);
}
@@ -434,12 +497,12 @@ public class LockTaskController {
task.userId,
lockTaskModeState));
}
-
- // Add it or move it to the top.
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
" Callers=" + Debug.getCallers(4));
- mLockTaskModeTasks.remove(task);
- mLockTaskModeTasks.add(task);
+
+ if (!mLockTaskModeTasks.contains(task)) {
+ mLockTaskModeTasks.add(task);
+ }
if (task.mLockTaskUid == -1) {
task.mLockTaskUid = task.effectiveUid;
@@ -556,8 +619,7 @@ public class LockTaskController {
}
mLockTaskFeatures.put(userId, flags);
- TaskRecord lockedTask = getLockedTask();
- if (lockedTask != null && userId == lockedTask.userId) {
+ if (!mLockTaskModeTasks.isEmpty() && userId == mLockTaskModeTasks.get(0).userId) {
mHandler.post(() -> {
if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
setStatusBarState(mLockTaskModeState, userId);
@@ -672,17 +734,6 @@ public class LockTaskController {
return mLockTaskFeatures.get(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
}
- /**
- * @return the topmost locked task
- */
- private TaskRecord getLockedTask() {
- final int top = mLockTaskModeTasks.size() - 1;
- if (top >= 0) {
- return mLockTaskModeTasks.get(top);
- }
- return null;
- }
-
// Should only be called on the handler thread
@Nullable
private IStatusBarService getStatusBarService() {
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 0b9e0a23a4e8..d35c37b54431 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -264,6 +264,20 @@ class RecentTasks {
return cn.equals(mRecentsComponent) && UserHandle.isSameApp(uid, mRecentsUid);
}
+ /**
+ * @return the recents component.
+ */
+ ComponentName getRecentsComponent() {
+ return mRecentsComponent;
+ }
+
+ /**
+ * @return the uid for the recents component.
+ */
+ int getRecentsComponentUid() {
+ return mRecentsUid;
+ }
+
void registerCallback(Callbacks callback) {
mCallbacks.add(callback);
}
diff --git a/services/core/java/com/android/server/am/RunningTasks.java b/services/core/java/com/android/server/am/RunningTasks.java
index 400b03a986ed..c860df89f29f 100644
--- a/services/core/java/com/android/server/am/RunningTasks.java
+++ b/services/core/java/com/android/server/am/RunningTasks.java
@@ -47,8 +47,10 @@ class RunningTasks {
void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
int callingUid, boolean allowed) {
- // For each stack on each display, add the tasks into the sorted set and then pull the first
- // {@param maxNum} from the set
+ // Return early if there are no tasks to fetch
+ if (maxNum <= 0) {
+ return;
+ }
// Gather all of the tasks across all of the tasks, and add them to the sorted set
mTmpSortedSet.clear();
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 1b5a1ce33cff..1a4f9d4fbd5b 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -438,7 +438,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
}
void removeWindowContainer() {
- mService.mLockTaskController.removeLockedTask(this);
+ mService.mLockTaskController.clearLockedTask(this);
mWindowContainerController.removeContainer();
if (!getWindowConfiguration().persistTaskBounds()) {
// Reset current bounds for task whose bounds shouldn't be persisted so it uses
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 44f83b0e08fb..2df5dc93768c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -872,9 +872,7 @@ class UserController implements Handler.Callback {
}
if (foreground) {
- // TODO: I don't think this does what the caller think it does. Seems to only
- // remove one locked task and won't work if multiple locked tasks are present.
- mInjector.clearLockTaskMode("startUser");
+ mInjector.clearAllLockedTasks("startUser");
}
final UserInfo userInfo = getUserInfo(userId);
@@ -2053,9 +2051,9 @@ class UserController implements Handler.Callback {
}
}
- protected void clearLockTaskMode(String reason) {
+ protected void clearAllLockedTasks(String reason) {
synchronized (mService) {
- mService.mLockTaskController.clearLockTaskMode(reason);
+ mService.mLockTaskController.clearLockedTasks(reason);
}
}
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 6e1c21eea15a..c4e6ff6bc906 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -163,10 +163,6 @@ public final class ContentService extends IContentService.Stub {
};
private SyncManager getSyncManager() {
- if (SystemProperties.getBoolean("config.disable_network", false)) {
- return null;
- }
-
synchronized(mSyncManagerLock) {
try {
// Try to create the SyncManager, return null if it fails (e.g. the disk is full).
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index ae01c433fc8d..a7e674b5a3a1 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -65,7 +65,7 @@ public final class JobServiceContext implements ServiceConnection {
private static final boolean DEBUG = JobSchedulerService.DEBUG;
private static final String TAG = "JobServiceContext";
/** Amount of time a job is allowed to execute for before being considered timed-out. */
- private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
+ public static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
/** Amount of time the JobScheduler waits for the initial service launch+bind. */
private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000;
/** Amount of time the JobScheduler will wait for a response from an app for a message. */
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 c928c07be983..ddee345a1c42 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -25,13 +25,16 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkPolicyManager;
+import android.net.TrafficStats;
import android.os.Process;
import android.os.UserHandle;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobServiceContext;
import com.android.server.job.StateChangedListener;
import java.io.PrintWriter;
@@ -99,18 +102,66 @@ public final class ConnectivityController extends StateController implements
}
}
+ /**
+ * Test to see if running the given job on the given network is sane.
+ * <p>
+ * For example, if a job is trying to send 10MB over a 128Kbps EDGE
+ * connection, it would take 10.4 minutes, and has no chance of succeeding
+ * before the job times out, so we'd be insane to try running it.
+ */
+ private boolean isSane(JobStatus jobStatus, NetworkCapabilities capabilities) {
+ final long estimatedBytes = jobStatus.getEstimatedNetworkBytes();
+ if (estimatedBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
+ // We don't know how large the job is; cross our fingers!
+ return true;
+ }
+ if (capabilities == null) {
+ // We don't know what the network is like; cross our fingers!
+ return true;
+ }
+
+ // We don't ask developers to differentiate between upstream/downstream
+ // in their size estimates, so test against the slowest link direction.
+ final long downstream = capabilities.getLinkDownstreamBandwidthKbps();
+ final long upstream = capabilities.getLinkUpstreamBandwidthKbps();
+ final long slowest;
+ if (downstream > 0 && upstream > 0) {
+ slowest = Math.min(downstream, upstream);
+ } else if (downstream > 0) {
+ slowest = downstream;
+ } else if (upstream > 0) {
+ slowest = upstream;
+ } else {
+ // We don't know what the network is like; cross our fingers!
+ return true;
+ }
+
+ final long estimatedMillis = ((estimatedBytes * DateUtils.SECOND_IN_MILLIS)
+ / (slowest * TrafficStats.KB_IN_BYTES / 8));
+ if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) {
+ // If we'd never finish before the timeout, we'd be insane!
+ Slog.w(TAG, "Estimated " + estimatedBytes + " bytes over " + slowest
+ + " kbps network would take " + estimatedMillis + "ms; that's insane!");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
final int jobUid = jobStatus.getSourceUid();
final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
- final NetworkInfo info = mConnManager.getActiveNetworkInfoForUid(jobUid, ignoreBlocked);
final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
+ final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked);
+
final NetworkCapabilities capabilities = (network != null)
? mConnManager.getNetworkCapabilities(network) : null;
+ final boolean connected = (info != null) && info.isConnected();
final boolean validated = (capabilities != null)
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
- final boolean connected = (info != null) && info.isConnected();
- final boolean connectionUsable = connected && validated;
+ final boolean sane = isSane(jobStatus, capabilities);
+ final boolean connectionUsable = connected && validated && sane;
final boolean metered = connected && (capabilities != null)
&& !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
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 1a27c0afb5ea..46ed84e599e8 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -219,6 +219,8 @@ public final class JobStatus {
*/
ContentObserverController.JobInstance contentObserverJobInstance;
+ private long totalNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+
/** Provide a handle to the service that this job will be run on. */
public int getServiceToken() {
return callingUid;
@@ -296,6 +298,8 @@ public final class JobStatus {
mLastSuccessfulRunTime = lastSuccessfulRunTime;
mLastFailedRunTime = lastFailedRunTime;
+
+ updateEstimatedNetworkBytesLocked();
}
/** Copy constructor: used specifically when cloning JobStatus objects for persistence,
@@ -389,6 +393,7 @@ public final class JobStatus {
sourcePackageName, sourceUserId, toShortString()));
}
pendingWork.add(work);
+ updateEstimatedNetworkBytesLocked();
}
public JobWorkItem dequeueWorkLocked() {
@@ -401,6 +406,7 @@ public final class JobStatus {
executingWork.add(work);
work.bumpDeliveryCount();
}
+ updateEstimatedNetworkBytesLocked();
return work;
}
return null;
@@ -459,6 +465,7 @@ public final class JobStatus {
pendingWork = null;
executingWork = null;
incomingJob.nextPendingWorkId = nextPendingWorkId;
+ incomingJob.updateEstimatedNetworkBytesLocked();
} else {
// We are completely stopping the job... need to clean up work.
ungrantWorkList(am, pendingWork);
@@ -466,6 +473,7 @@ public final class JobStatus {
ungrantWorkList(am, executingWork);
executingWork = null;
}
+ updateEstimatedNetworkBytesLocked();
}
public void prepareLocked(IActivityManager am) {
@@ -568,6 +576,38 @@ public final class JobStatus {
return job.getFlags();
}
+ private void updateEstimatedNetworkBytesLocked() {
+ totalNetworkBytes = computeEstimatedNetworkBytesLocked();
+ }
+
+ private long computeEstimatedNetworkBytesLocked() {
+ // If any component of the job has unknown usage, we don't have a
+ // complete picture of what data will be used, and we have to treat the
+ // entire job as unknown.
+ long totalNetworkBytes = 0;
+ long networkBytes = job.getEstimatedNetworkBytes();
+ if (networkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
+ return JobInfo.NETWORK_BYTES_UNKNOWN;
+ } else {
+ totalNetworkBytes += networkBytes;
+ }
+ if (pendingWork != null) {
+ for (int i = 0; i < pendingWork.size(); i++) {
+ networkBytes = pendingWork.get(i).getEstimatedNetworkBytes();
+ if (networkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
+ return JobInfo.NETWORK_BYTES_UNKNOWN;
+ } else {
+ totalNetworkBytes += networkBytes;
+ }
+ }
+ }
+ return totalNetworkBytes;
+ }
+
+ public long getEstimatedNetworkBytes() {
+ return totalNetworkBytes;
+ }
+
/** Does this job have any sort of networking constraint? */
public boolean hasConnectivityConstraint() {
return (requiredConstraints&CONNECTIVITY_MASK) != 0;
@@ -1047,6 +1087,9 @@ public final class JobStatus {
if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType());
}
+ if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ pw.print(prefix); pw.print(" Network bytes: "); pw.println(totalNetworkBytes);
+ }
if (job.getMinLatencyMillis() != 0) {
pw.print(prefix); pw.print(" Minimum latency: ");
TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index 2493dfb42a60..d50ffe965c7f 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -145,7 +145,8 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
*/
private void updateMinInterval() {
mEffectiveMinIntervalMs = Settings.Global.getLong(mResolver,
- Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, DEFAULT_MIN_INTERVAL_MS);
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
+ DEFAULT_MIN_INTERVAL_MS);
}
public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 4cf35bc4accb..660659fdfa5c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -803,18 +803,6 @@ public class GnssLocationProvider implements LocationProviderInterface {
}
};
mGnssMetrics = new GnssMetrics();
-
- /*
- * A cycle of native_init() and native_cleanup() is needed so that callbacks are registered
- * after bootup even when location is disabled. This will allow Emergency SUPL to work even
- * when location is disabled before device restart.
- * */
- boolean isInitialized = native_init();
- if(!isInitialized) {
- Log.d(TAG, "Failed to initialize at bootup");
- } else {
- native_cleanup();
- }
}
/**
@@ -2272,6 +2260,19 @@ public class GnssLocationProvider implements LocationProviderInterface {
* this handler.
*/
private void handleInitialize() {
+ /*
+ * A cycle of native_init() and native_cleanup() is needed so that callbacks are
+ * registered after bootup even when location is disabled.
+ * This will allow Emergency SUPL to work even when location is disabled before device
+ * restart.
+ */
+ boolean isInitialized = native_init();
+ if(!isInitialized) {
+ Log.w(TAG, "Native initialization failed at bootup");
+ } else {
+ native_cleanup();
+ }
+
// load default GPS configuration
// (this configuration might change in the future based on SIM changes)
reloadGpsProperties(mContext, mProperties);
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 061fd8da9d60..6b77d8326a39 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -2,7 +2,7 @@ set noparent
ek@google.com
hugobenichi@google.com
-jsharkey@google.com
+jsharkey@android.com
lorenzo@google.com
satk@google.com
silberst@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 238d87b7c396..6cebdd64d8f9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -460,7 +460,7 @@ public class NotificationManagerService extends SystemService {
mRankingHelper.readXml(parser, forRestore);
}
// No non-system managed services are allowed on low ram devices
- if (!ActivityManager.isLowRamDeviceStatic()) {
+ if (canUseManagedServices()) {
if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
mListeners.readXml(parser);
migratedManagedServices = true;
@@ -548,12 +548,6 @@ public class NotificationManagerService extends SystemService {
out.endDocument();
}
- /** Use this to check if a package can post a notification or toast. */
- private boolean checkNotificationOp(String pkg, int uid) {
- return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
- == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
- }
-
private static final class ToastRecord
{
final int pid;
@@ -1226,7 +1220,6 @@ public class NotificationManagerService extends SystemService {
mAccessibilityManager = am;
}
-
// TODO: All tests should use this init instead of the one-off setters above.
@VisibleForTesting
void init(Looper looper, IPackageManager packageManager,
@@ -2818,19 +2811,25 @@ public class NotificationManagerService extends SystemService {
@Override
public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
throws RemoteException {
+ setNotificationPolicyAccessGrantedForUser(
+ pkg, getCallingUserHandle().getIdentifier(), granted);
+ }
+
+ @Override
+ public void setNotificationPolicyAccessGrantedForUser(
+ String pkg, int userId, boolean granted) {
checkCallerIsSystemOrShell();
final long identity = Binder.clearCallingIdentity();
try {
- if (!mActivityManager.isLowRamDevice()) {
+ if (canUseManagedServices()) {
mConditionProviders.setPackageOrComponentEnabled(
- pkg, getCallingUserHandle().getIdentifier(), true, granted);
+ pkg, userId, true, granted);
getContext().sendBroadcastAsUser(new Intent(
NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
.setPackage(pkg)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
- getCallingUserHandle(), null);
-
+ UserHandle.of(userId), null);
savePolicyFile();
}
} finally {
@@ -2918,18 +2917,17 @@ public class NotificationManagerService extends SystemService {
checkCallerIsSystemOrShell();
final long identity = Binder.clearCallingIdentity();
try {
- if (!mActivityManager.isLowRamDevice()) {
+ if (canUseManagedServices()) {
mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
userId, false, granted);
mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
userId, true, granted);
getContext().sendBroadcastAsUser(new Intent(
- NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
-
+ NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
.setPackage(listener.getPackageName())
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
- getCallingUserHandle(), null);
+ UserHandle.of(userId), null);
savePolicyFile();
}
@@ -2945,7 +2943,7 @@ public class NotificationManagerService extends SystemService {
checkCallerIsSystemOrShell();
final long identity = Binder.clearCallingIdentity();
try {
- if (!mActivityManager.isLowRamDevice()) {
+ if (canUseManagedServices()) {
mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
userId, false, granted);
mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
@@ -2955,7 +2953,7 @@ public class NotificationManagerService extends SystemService {
NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
.setPackage(assistant.getPackageName())
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
- getCallingUserHandle(), null);
+ UserHandle.of(userId), null);
savePolicyFile();
}
@@ -5434,6 +5432,11 @@ public class NotificationManagerService extends SystemService {
}
}
+ private boolean canUseManagedServices() {
+ return !mActivityManager.isLowRamDevice()
+ || mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
private class TrimCache {
StatusBarNotification heavy;
StatusBarNotification sbnClone;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 86a1c03d2829..50ac409f31c9 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -209,7 +209,7 @@ public class PackageDexOptimizer {
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
// flags.
- final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
+ final int dexoptFlags = getDexFlags(pkg, compilerFilter, options);
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
@@ -349,8 +349,7 @@ public class PackageDexOptimizer {
dexUseInfo.isUsedByOtherApps());
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
// Secondary dex files are currently not compiled at boot.
- int dexoptFlags = getDexFlags(info, compilerFilter, /* bootComplete */ true)
- | DEXOPT_SECONDARY_DEX;
+ int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX;
// Check the app storage and add the appropriate flags.
if (info.deviceProtectedDataDir != null &&
FileUtils.contains(info.deviceProtectedDataDir, path)) {
@@ -486,11 +485,11 @@ public class PackageDexOptimizer {
* filter.
*/
private int getDexFlags(PackageParser.Package pkg, String compilerFilter,
- boolean bootComplete) {
- return getDexFlags(pkg.applicationInfo, compilerFilter, bootComplete);
+ DexoptOptions options) {
+ return getDexFlags(pkg.applicationInfo, compilerFilter, options);
}
- private int getDexFlags(ApplicationInfo info, String compilerFilter, boolean bootComplete) {
+ private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Profile guide compiled oat files should not be public.
@@ -501,7 +500,8 @@ public class PackageDexOptimizer {
(isPublic ? DEXOPT_PUBLIC : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
| profileFlag
- | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0);
+ | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
+ | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
return adjustDexoptFlags(dexFlags);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7be0cde4a94d..7837b029830f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8687,6 +8687,7 @@ public class PackageManagerService extends IPackageManager.Stub
pkg.applicationInfo.primaryCpuAbi = updatedPkg.primaryCpuAbiString;
pkg.applicationInfo.secondaryCpuAbi = updatedPkg.secondaryCpuAbiString;
}
+ pkg.mExtras = updatedPkg;
throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
+ scanFile + " ignored: updated version " + ps.versionCode
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 19b0d9bc4b90..cf9e6f3c0c98 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -52,7 +52,16 @@ public class PackageManagerServiceCompilerMapping {
// Load the property for the given reason and check for validity. This will throw an
// exception in case the reason or value are invalid.
private static String getAndCheckValidity(int reason) {
- String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
+ String sysPropName = getSystemPropertyName(reason);
+ String sysPropValue;
+ // TODO: This is a temporary hack to keep marlin booting on aosp/master while we
+ // figure out how to deal with these system properties that currently appear on
+ // vendor.
+ if ("pm.dexopt.inactive".equals(sysPropName)) {
+ sysPropValue = "verify";
+ } else {
+ sysPropValue = SystemProperties.get(sysPropName);
+ }
if (sysPropValue == null || sysPropValue.isEmpty() ||
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a2099e6080e5..807eb1a8aac4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -54,6 +54,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IUserManager;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -78,7 +79,6 @@ import dalvik.system.DexFile;
import libcore.io.IoUtils;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -102,8 +102,6 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO
class PackageManagerShellCommand extends ShellCommand {
/** Path for streaming APK content */
private static final String STDIN_PATH = "-";
- /** Whether or not APK content must be streamed from stdin */
- private static final boolean FORCE_STREAM_INSTALL = true;
final IPackageManager mInterface;
final private WeakHashMap<String, Resources> mResourceCache =
@@ -255,30 +253,27 @@ class PackageManagerShellCommand extends ShellCommand {
}
private void setParamsSize(InstallParams params, String inPath) {
- // If we're forced to stream the package, the params size
- // must be set via command-line argument. There's nothing
- // to do here.
- if (FORCE_STREAM_INSTALL) {
- return;
- }
- final PrintWriter pw = getOutPrintWriter();
if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
- File file = new File(inPath);
- if (file.isFile()) {
+ final ParcelFileDescriptor fd = openFileForSystem(inPath, "r");
+ if (fd == null) {
+ getErrPrintWriter().println("Error: Can't open file: " + inPath);
+ throw new IllegalArgumentException("Error: Can't open file: " + inPath);
+ }
+ try {
+ ApkLite baseApk = PackageParser.parseApkLite(fd.getFileDescriptor(), inPath, 0);
+ PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+ null, null);
+ params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
+ pkgLite, params.sessionParams.abiOverride));
+ } catch (PackageParserException | IOException e) {
+ getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath);
+ throw new IllegalArgumentException(
+ "Error: Failed to parse APK file: " + inPath, e);
+ } finally {
try {
- ApkLite baseApk = PackageParser.parseApkLite(file, 0);
- PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
- null, null);
- params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
- pkgLite, params.sessionParams.abiOverride));
- } catch (PackageParserException | IOException e) {
- pw.println("Error: Failed to parse APK file: " + file);
- throw new IllegalArgumentException(
- "Error: Failed to parse APK file: " + file, e);
+ fd.close();
+ } catch (IOException e) {
}
- } else {
- pw.println("Error: Can't open non-file: " + inPath);
- throw new IllegalArgumentException("Error: Can't open non-file: " + inPath);
}
}
}
@@ -1914,6 +1909,12 @@ class PackageManagerShellCommand extends ShellCommand {
throw new IllegalArgumentException("Missing inherit package name");
}
break;
+ case "--pkg":
+ sessionParams.appPackageName = getNextArg();
+ if (sessionParams.appPackageName == null) {
+ throw new IllegalArgumentException("Missing package name");
+ }
+ break;
case "-S":
final long sizeBytes = Long.parseLong(getNextArg());
if (sizeBytes <= 0) {
@@ -1925,6 +1926,7 @@ class PackageManagerShellCommand extends ShellCommand {
sessionParams.abiOverride = checkAbiArgument(getNextArg());
break;
case "--ephemeral":
+ case "--instant":
case "--instantapp":
sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);
break;
@@ -2092,20 +2094,24 @@ class PackageManagerShellCommand extends ShellCommand {
private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
- if (FORCE_STREAM_INSTALL && inPath != null && !STDIN_PATH.equals(inPath)) {
- pw.println("Error: APK content must be streamed");
- return 1;
- }
+ final ParcelFileDescriptor fd;
if (STDIN_PATH.equals(inPath)) {
- inPath = null;
+ fd = null;
} else if (inPath != null) {
- final File file = new File(inPath);
- if (file.isFile()) {
- sizeBytes = file.length();
+ fd = openFileForSystem(inPath, "r");
+ if (fd == null) {
+ return -1;
+ }
+ sizeBytes = fd.getStatSize();
+ if (sizeBytes < 0) {
+ getErrPrintWriter().println("Unable to get size of: " + inPath);
+ return -1;
}
+ } else {
+ fd = null;
}
if (sizeBytes <= 0) {
- pw.println("Error: must specify a APK size");
+ getErrPrintWriter().println("Error: must specify a APK size");
return 1;
}
@@ -2118,8 +2124,8 @@ class PackageManagerShellCommand extends ShellCommand {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
- if (inPath != null) {
- in = new FileInputStream(inPath);
+ if (fd != null) {
+ in = new ParcelFileDescriptor.AutoCloseInputStream(fd);
} else {
in = new SizedInputStream(getRawInputStream(), sizeBytes);
}
@@ -2144,7 +2150,7 @@ class PackageManagerShellCommand extends ShellCommand {
}
return 0;
} catch (IOException e) {
- pw.println("Error: failed to write; " + e.getMessage());
+ getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
return 1;
} finally {
IoUtils.closeQuietly(out);
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 533b261998ae..8014acf653ee 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -383,6 +383,7 @@ public final class DefaultPermissionGrantPolicy {
MediaStore.AUTHORITY, userId);
if (mediaStorePackage != null) {
grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
+ grantRuntimePermissions(mediaStorePackage, PHONE_PERMISSIONS, true, userId);
}
// Downloads provider
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
new file mode 100644
index 000000000000..6c8c9b20ecdb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -0,0 +1,7 @@
+per-file DefaultPermissionGrantPolicy.java = bpoiesz@google.com
+per-file DefaultPermissionGrantPolicy.java = fkupolov@google.com
+per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
+per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
+per-file DefaultPermissionGrantPolicy.java = svetoslavganov@google.com
+per-file DefaultPermissionGrantPolicy.java = toddke@google.com
+per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 22d2bcfc3e7e..41534cbb641f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -24,7 +24,8 @@ import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.IntentFilter;
+import android.net.NetworkStats;
+import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -37,15 +38,17 @@ import android.os.StatsLogEventWrapper;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.List;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
/**
@@ -65,7 +68,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private static final Object sStatsdLock = new Object();
private final PendingIntent mAnomalyAlarmIntent;
- private final PendingIntent mPollingAlarmIntent;
+ private final PendingIntent mPullingAlarmIntent;
private final BroadcastReceiver mAppUpdateReceiver;
private final BroadcastReceiver mUserUpdateReceiver;
@@ -76,8 +79,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
- mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
- new Intent(mContext, PollingAlarmReceiver.class), 0);
+ mPullingAlarmIntent = PendingIntent.getBroadcast(
+ mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -144,15 +147,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
public final static class AppUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
/**
* App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
* waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
+ * If we can't find the value for EXTRA_REPLACING, we default to false.
*/
- if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
- intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
+ && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
return; // Keep only replacing or normal add and remove.
}
+ Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
synchronized (sStatsdLock) {
if (sStatsd == null) {
Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
@@ -205,24 +209,25 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- public final static class PollingAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) Slog.d(TAG, "Time to poll something.");
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of polling alarm firing");
- return;
- }
- try {
- // Two-way call to statsd to retain AlarmManager wakelock
- sStatsd.informPollAlarmFired();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to inform statsd of polling alarm firing", e);
- }
- }
- // AlarmManager releases its own wakelock here.
+ public final static class PullingAlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG)
+ Slog.d(TAG, "Time to poll something.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informPollAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of pulling alarm firing", e);
+ }
}
+ // AlarmManager releases its own wakelock here.
+ }
}
@Override // Binder call
@@ -253,32 +258,32 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
@Override // Binder call
- public void setPollingAlarms(long timestampMs, long intervalMs) {
- enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "Setting polling alarm for " + timestampMs
- + " every " + intervalMs + "ms");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
- // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
- // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
- mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs,
- mPollingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void setPullingAlarms(long timestampMs, long intervalMs) {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
+ // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+ // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
+ mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
@Override // Binder call
- public void cancelPollingAlarms() {
- enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "Cancelling polling alarm");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPollingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void cancelPullingAlarms() {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Cancelling pulling alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
// These values must be kept in sync with cmd/statsd/StatsPullerManager.h.
@@ -288,35 +293,168 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+ private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
+ List<StatsLogEventWrapper> ret = new ArrayList<>();
+ int size = stats.size();
+ NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
+ for (int j = 0; j < size; j++) {
+ stats.getValues(j, entry);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
+ e.writeInt(entry.uid);
+ if (withFGBG) {
+ e.writeInt(entry.set);
+ }
+ e.writeLong(entry.rxBytes);
+ e.writeLong(entry.rxPackets);
+ e.writeLong(entry.txBytes);
+ e.writeLong(entry.txPackets);
+ ret.add(e);
+ }
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+ }
+
+ /**
+ * Allows rollups per UID but keeping the set (foreground/background) slicing.
+ * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
+ */
+ private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
+ final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
+
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ entry.iface = NetworkStats.IFACE_ALL;
+ entry.tag = NetworkStats.TAG_NONE;
+ entry.metered = NetworkStats.METERED_ALL;
+ entry.roaming = NetworkStats.ROAMING_ALL;
+
+ int size = stats.size();
+ NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
+ for (int i = 0; i < size; i++) {
+ stats.getValues(i, recycle);
+
+ // Skip specific tags, since already counted in TAG_NONE
+ if (recycle.tag != NetworkStats.TAG_NONE) continue;
+
+ entry.set = recycle.set; // Allows slicing by background/foreground
+ entry.uid = recycle.uid;
+ entry.rxBytes = recycle.rxBytes;
+ entry.rxPackets = recycle.rxPackets;
+ entry.txBytes = recycle.txBytes;
+ entry.txPackets = recycle.txPackets;
+ // Operations purposefully omitted since we don't use them for statsd.
+ ret.combineValues(entry);
+ }
+ return ret;
+ }
+
@Override // Binder call
public StatsLogEventWrapper[] pullData(int pullCode) {
enforceCallingPermission();
- if (DEBUG) {
+ if (DEBUG)
Slog.d(TAG, "Pulling " + pullCode);
- }
- List<StatsLogEventWrapper> ret = new ArrayList<>();
switch (pullCode) {
- case PULL_CODE_KERNEL_WAKELOCKS: {
+ case StatsLog.WIFI_BYTES_TRANSFERRED: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Consider caching the following call to get BatteryStatsInternal.
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null).groupedByUid();
+ return addNetworkStats(pullCode, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.MOBILE_BYTES_TRANSFERRED: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null).groupedByUid();
+ return addNetworkStats(pullCode, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.WIFI_BYTES_TRANSFERRED_BY_FG_BG: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null));
+ return addNetworkStats(pullCode, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.MOBILE_BYTES_TRANSFERRED_BY_FG_BG: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null));
+ return addNetworkStats(pullCode, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.KERNEL_WAKELOCKS_REPORTED: {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+ List<StatsLogEventWrapper> ret = new ArrayList();
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(101, 4);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(pullCode, 4);
+ e.writeString(name);
e.writeInt(kws.mCount);
e.writeInt(kws.mVersion);
e.writeLong(kws.mTotalTime);
- e.writeString(name);
ret.add(e);
}
- break;
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
}
default:
Slog.w(TAG, "No such pollable data as " + pullCode);
return null;
}
- return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+ return null;
}
@Override // Binder call
@@ -440,7 +578,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
mContext.unregisterReceiver(mAppUpdateReceiver);
mContext.unregisterReceiver(mUserUpdateReceiver);
cancelAnomalyAlarm();
- cancelPollingAlarms();
+ cancelPullingAlarms();
}
}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index e7e4efccea2f..e8ebbe4dd805 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -166,6 +166,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC
private boolean mUserUnlocked;
private Vr2dDisplay mVr2dDisplay;
private boolean mBootsToVr;
+ private boolean mStandby;
+ private boolean mUseStandbyToExitVrMode;
// Handles events from the managed services (e.g. VrListenerService and any bound VR compositor
// service).
@@ -203,7 +205,10 @@ public class VrManagerService extends SystemService implements EnabledComponentC
*
*/
private void updateVrModeAllowedLocked() {
- boolean allowed = mSystemSleepFlags == FLAG_ALL && mUserUnlocked;
+ boolean ignoreSleepFlags = mBootsToVr && mUseStandbyToExitVrMode;
+ boolean disallowedByStandby = mStandby && mUseStandbyToExitVrMode;
+ boolean allowed = (mSystemSleepFlags == FLAG_ALL || ignoreSleepFlags) && mUserUnlocked
+ && !disallowedByStandby;
if (mVrModeAllowed != allowed) {
mVrModeAllowed = allowed;
if (DBG) Slog.d(TAG, "VR mode is " + ((allowed) ? "allowed" : "disallowed"));
@@ -273,6 +278,17 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
}
+ private void setStandbyEnabled(boolean standby) {
+ synchronized(mLock) {
+ if (!mBootsToVr) {
+ Slog.e(TAG, "Attempting to set standby mode on a non-standalone device");
+ return;
+ }
+ mStandby = standby;
+ updateVrModeAllowedLocked();
+ }
+ }
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -587,6 +603,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
@Override
+ public void setStandbyEnabled(boolean standby) {
+ enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER);
+ VrManagerService.this.setStandbyEnabled(standby);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -733,6 +755,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC
}
mBootsToVr = SystemProperties.getBoolean("ro.boot.vr", false);
+ mUseStandbyToExitVrMode = mBootsToVr
+ && SystemProperties.getBoolean("persist.vr.use_standby_to_exit_vr_mode", false);
publishLocalService(VrManagerInternal.class, new LocalService());
publishBinderService(Context.VR_SERVICE, mVrManager.asBinder());
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e873d32e7278..98db80ef0a3b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.os.Build.VERSION_CODES.O_MR1;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
@@ -1101,52 +1100,54 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
+ " from " + fromToken + " to " + this);
final long origId = Binder.clearCallingIdentity();
+ try {
+ // Transfer the starting window over to the new token.
+ startingData = fromToken.startingData;
+ startingSurface = fromToken.startingSurface;
+ startingDisplayed = fromToken.startingDisplayed;
+ fromToken.startingDisplayed = false;
+ startingWindow = tStartingWindow;
+ reportedVisible = fromToken.reportedVisible;
+ fromToken.startingData = null;
+ fromToken.startingSurface = null;
+ fromToken.startingWindow = null;
+ fromToken.startingMoved = true;
+ tStartingWindow.mToken = this;
+ tStartingWindow.mAppToken = this;
+
+ if (DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+ "Removing starting " + tStartingWindow + " from " + fromToken);
+ fromToken.removeChild(tStartingWindow);
+ fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow);
+ fromToken.mHiddenSetFromTransferredStartingWindow = false;
+ addWindow(tStartingWindow);
+
+ // Propagate other interesting state between the tokens. If the old token is displayed,
+ // we should immediately force the new one to be displayed. If it is animating, we need
+ // to move that animation to the new one.
+ if (fromToken.allDrawn) {
+ allDrawn = true;
+ deferClearAllDrawn = fromToken.deferClearAllDrawn;
+ }
+ if (fromToken.firstWindowDrawn) {
+ firstWindowDrawn = true;
+ }
+ if (!fromToken.hidden) {
+ hidden = false;
+ hiddenRequested = false;
+ mHiddenSetFromTransferredStartingWindow = true;
+ }
+ setClientHidden(fromToken.mClientHidden);
+ fromToken.mAppAnimator.transferCurrentAnimation(
+ mAppAnimator, tStartingWindow.mWinAnimator);
- // Transfer the starting window over to the new token.
- startingData = fromToken.startingData;
- startingSurface = fromToken.startingSurface;
- startingDisplayed = fromToken.startingDisplayed;
- fromToken.startingDisplayed = false;
- startingWindow = tStartingWindow;
- reportedVisible = fromToken.reportedVisible;
- fromToken.startingData = null;
- fromToken.startingSurface = null;
- fromToken.startingWindow = null;
- fromToken.startingMoved = true;
- tStartingWindow.mToken = this;
- tStartingWindow.mAppToken = this;
-
- if (DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Removing starting " + tStartingWindow + " from " + fromToken);
- fromToken.removeChild(tStartingWindow);
- fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow);
- fromToken.mHiddenSetFromTransferredStartingWindow = false;
- addWindow(tStartingWindow);
-
- // Propagate other interesting state between the tokens. If the old token is displayed,
- // we should immediately force the new one to be displayed. If it is animating, we need
- // to move that animation to the new one.
- if (fromToken.allDrawn) {
- allDrawn = true;
- deferClearAllDrawn = fromToken.deferClearAllDrawn;
- }
- if (fromToken.firstWindowDrawn) {
- firstWindowDrawn = true;
- }
- if (!fromToken.hidden) {
- hidden = false;
- hiddenRequested = false;
- mHiddenSetFromTransferredStartingWindow = true;
+ mService.updateFocusedWindowLocked(
+ UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
+ getDisplayContent().setLayoutNeeded();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- setClientHidden(fromToken.mClientHidden);
- fromToken.mAppAnimator.transferCurrentAnimation(
- mAppAnimator, tStartingWindow.mWinAnimator);
-
- mService.updateFocusedWindowLocked(
- UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
- getDisplayContent().setLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
- Binder.restoreCallingIdentity(origId);
return true;
} else if (fromToken.startingData != null) {
// The previous app was getting ready to show a
@@ -1201,15 +1202,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
*/
@Override
int getOrientation(int candidate) {
- // We do not allow non-fullscreen apps to influence orientation starting in O-MR1. While we
- // do throw an exception in {@link Activity#onCreate} and
- // {@link Activity#setRequestedOrientation}, we also ignore the orientation here so that
- // other calculations aren't affected.
- if (!fillsParent() && mTargetSdk >= O_MR1) {
- // Can't specify orientation if app doesn't fill parent.
- return SCREEN_ORIENTATION_UNSET;
- }
-
if (candidate == SCREEN_ORIENTATION_BEHIND) {
// Allow app to specify orientation regardless of its visibility state if the current
// candidate want us to use orientation behind. I.e. the visible app on-top of this one
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 401547e6eebb..8fb2be8c256f 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -119,7 +119,7 @@ public class DimLayer {
} catch (Exception e) {
Slog.e(TAG_WM, "Exception creating Dim surface", e);
} finally {
- service.closeSurfaceTransaction();
+ service.closeSurfaceTransaction("DimLayer.constructSurface");
}
}
@@ -235,7 +235,7 @@ public class DimLayer {
} catch (RuntimeException e) {
Slog.w(TAG, "Failure setting size", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("DimLayer.setBounds");
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4c6ab3f7cc4f..2f54e0e18b8b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1083,7 +1083,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mService.mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
} finally {
if (!inTransaction) {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setRotationUnchecked");
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, "<<< CLOSE TRANSACTION setRotationUnchecked");
}
@@ -1452,17 +1452,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/**
* @return The primary split-screen stack, but only if it is visible, and {@code null} otherwise.
*/
- TaskStack getSplitScreenPrimaryStackStack() {
- TaskStack stack = mTaskStackContainers.getSplitScreenPrimaryStackStack();
+ TaskStack getSplitScreenPrimaryStack() {
+ TaskStack stack = mTaskStackContainers.getSplitScreenPrimaryStack();
return (stack != null && stack.isVisible()) ? stack : null;
}
/**
- * Like {@link #getSplitScreenPrimaryStackStack}, but also returns the stack if it's currently
+ * Like {@link #getSplitScreenPrimaryStack}, but also returns the stack if it's currently
* not visible.
*/
- TaskStack getSplitScreenPrimaryStackStackIgnoringVisibility() {
- return mTaskStackContainers.getSplitScreenPrimaryStackStack();
+ TaskStack getSplitScreenPrimaryStackIgnoringVisibility() {
+ return mTaskStackContainers.getSplitScreenPrimaryStack();
}
TaskStack getPinnedStack() {
@@ -1877,7 +1877,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
}
// TODO(multi-display): Support docked stacks on secondary displays.
- if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStackStack() != null) {
+ if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStack() != null) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
mTmpRegion.set(mTmpRect);
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -2232,7 +2232,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (pinnedStack != null) {
pw.println(prefix + "pinnedStack=" + pinnedStack.getName());
}
- final TaskStack splitScreenPrimaryStack = getSplitScreenPrimaryStackStack();
+ final TaskStack splitScreenPrimaryStack = getSplitScreenPrimaryStack();
if (splitScreenPrimaryStack != null) {
pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName());
}
@@ -3401,7 +3401,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mPinnedStack;
}
- TaskStack getSplitScreenPrimaryStackStack() {
+ TaskStack getSplitScreenPrimaryStack() {
return mSplitScreenPrimaryStack;
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5ce4d46c6b4a..5ea0e1d041ba 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -321,7 +321,7 @@ public class DockedStackDividerController implements DimLayerUser {
if (mWindow == null) {
return;
}
- TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
// If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
final boolean visible = stack != null;
@@ -361,7 +361,7 @@ public class DockedStackDividerController implements DimLayerUser {
}
void positionDockedStackedDivider(Rect frame) {
- TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStack();
+ TaskStack stack = mDisplayContent.getSplitScreenPrimaryStack();
if (stack == null) {
// Unfortunately we might end up with still having a divider, even though the underlying
// stack was already removed. This is because we are on AM thread and the removal of the
@@ -458,7 +458,7 @@ public class DockedStackDividerController implements DimLayerUser {
long animDuration = 0;
if (animate) {
final TaskStack stack =
- mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
final long transitionDuration = isAnimationMaximizing()
? mService.mAppTransition.getLastClipRevealTransitionDuration()
: DEFAULT_APP_TRANSITION_DURATION;
@@ -513,7 +513,7 @@ public class DockedStackDividerController implements DimLayerUser {
mDockedStackListeners.register(listener);
notifyDockedDividerVisibilityChanged(wasVisible());
notifyDockedStackExistsChanged(
- mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() != null);
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() != null);
notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
isHomeStackResizable());
notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
@@ -531,7 +531,7 @@ public class DockedStackDividerController implements DimLayerUser {
final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
? mDisplayContent.getStack(targetWindowingMode)
: null;
- final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackStack();
+ final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
stack.getDimBounds(mTmpRect);
@@ -543,7 +543,7 @@ public class DockedStackDividerController implements DimLayerUser {
mDimLayer.setBounds(mTmpRect);
mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setResizeDimLayer");
}
}
mLastDimLayerRect.set(mTmpRect);
@@ -558,7 +558,7 @@ public class DockedStackDividerController implements DimLayerUser {
mService.openSurfaceTransaction();
mDimLayer.hide();
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setResizeDimLayer");
}
}
mLastDimLayerAlpha = 0f;
@@ -616,7 +616,7 @@ public class DockedStackDividerController implements DimLayerUser {
}
private void checkMinimizeChanged(boolean animate) {
- if (mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() == null) {
+ if (mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() == null) {
return;
}
final TaskStack homeStack = mDisplayContent.getHomeStack();
@@ -778,7 +778,7 @@ public class DockedStackDividerController implements DimLayerUser {
}
private boolean setMinimizedDockedStack(boolean minimized) {
- final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
}
@@ -829,7 +829,7 @@ public class DockedStackDividerController implements DimLayerUser {
}
private boolean animateForMinimizedDockedStack(long now) {
- final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
if (!mAnimationStarted) {
mAnimationStarted = true;
mAnimationStartTime = now;
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index a3c6167075d9..79d46ce51326 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -24,7 +24,9 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.content.ClipData;
import android.graphics.PixelFormat;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.util.Slog;
import android.view.Display;
@@ -33,7 +35,6 @@ import android.view.Surface;
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.
@@ -41,13 +42,28 @@ import com.android.server.wm.WindowManagerService.H;
class DragDropController {
private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
private static final long DRAG_TIMEOUT_MS = 5000;
+
+ // Messages for Handler.
+ private static final int MSG_DRAG_START_TIMEOUT = 0;
+ static final int MSG_DRAG_END_TIMEOUT = 1;
+ static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2;
+ static final int MSG_ANIMATION_END = 3;
+
DragState mDragState;
+ private WindowManagerService mService;
+ private final Handler mHandler;
+
boolean dragDropActiveLocked() {
return mDragState != null;
}
- IBinder prepareDrag(WindowManagerService service, SurfaceSession session, int callerPid,
+ DragDropController(WindowManagerService service, Looper looper) {
+ mService = service;
+ mHandler = new DragHandler(service, looper);
+ }
+
+ IBinder prepareDrag(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
@@ -57,7 +73,7 @@ class DragDropController {
IBinder token = null;
- synchronized (service.mWindowMap) {
+ synchronized (mService.mWindowMap) {
if (dragDropActiveLocked()) {
Slog.w(TAG_WM, "Drag already in progress");
return null;
@@ -65,7 +81,7 @@ class DragDropController {
// TODO(multi-display): support other displays
final DisplayContent displayContent =
- service.getDefaultDisplayContentLocked();
+ mService.getDefaultDisplayContentLocked();
final Display display = displayContent.getDisplay();
final SurfaceControl surface = new SurfaceControl.Builder(session)
@@ -84,29 +100,27 @@ class DragDropController {
outSurface.copyFrom(surface);
final IBinder winBinder = window.asBinder();
token = new Binder();
- mDragState = new DragState(service, token, surface, flags, winBinder);
+ mDragState = new DragState(mService, 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
- 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);
+ sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
}
return token;
}
- boolean performDrag(WindowManagerService service, IWindow window, IBinder dragToken,
+ 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 (service.mWindowMap) {
+ synchronized (mService.mWindowMap) {
if (mDragState == null) {
Slog.w(TAG_WM, "No drag prepared");
throw new IllegalStateException("performDrag() without prepareDrag()");
@@ -117,7 +131,7 @@ class DragDropController {
throw new IllegalStateException("performDrag() does not match prepareDrag()");
}
- final WindowState callingWin = service.windowForClientLocked(null, window, false);
+ final WindowState callingWin = mService.windowForClientLocked(null, window, false);
if (callingWin == null) {
Slog.w(TAG_WM, "Bad requesting window " + window);
return false; // !!! TODO: throw here?
@@ -127,12 +141,12 @@ class DragDropController {
// 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());
+ mHandler.removeMessages(MSG_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
+ // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
// the actual drag event dispatch stuff in the dragstate
final DisplayContent displayContent = callingWin.getDisplayContent();
@@ -141,7 +155,7 @@ class DragDropController {
}
Display display = displayContent.getDisplay();
mDragState.register(display);
- if (!service.mInputManager.transferTouchFocus(callingWin.mInputChannel,
+ if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mDragState.getInputChannel())) {
Slog.e(TAG_WM, "Unable to transfer touch focus");
mDragState.unregister();
@@ -152,8 +166,8 @@ class DragDropController {
mDragState.mDisplayContent = displayContent;
mDragState.mData = data;
- mDragState.broadcastDragStartedLw(touchX, touchY);
- mDragState.overridePointerIconLw(touchSource);
+ mDragState.broadcastDragStartedLocked(touchX, touchY);
+ mDragState.overridePointerIconLocked(touchSource);
// remember the thumb offsets for later
mDragState.mThumbOffsetX = thumbCenterX;
@@ -163,32 +177,32 @@ class DragDropController {
final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
TAG_WM, ">>> OPEN TRANSACTION performDrag");
- service.openSurfaceTransaction();
+ mService.openSurfaceTransaction();
try {
surfaceControl.setPosition(touchX - thumbCenterX,
touchY - thumbCenterY);
- surfaceControl.setLayer(mDragState.getDragLayerLw());
+ surfaceControl.setLayer(mDragState.getDragLayerLocked());
surfaceControl.setLayerStack(display.getLayerStack());
surfaceControl.show();
} finally {
- service.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("performDrag");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
TAG_WM, "<<< CLOSE TRANSACTION performDrag");
}
- mDragState.notifyLocationLw(touchX, touchY);
+ mDragState.notifyLocationLocked(touchX, touchY);
}
return true; // success!
}
- void reportDropResult(WindowManagerService service, IWindow window, boolean consumed) {
+ void reportDropResult(IWindow window, boolean consumed) {
IBinder token = window.asBinder();
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
}
- synchronized (service.mWindowMap) {
+ synchronized (mService.mWindowMap) {
if (mDragState == null) {
// Most likely the drop recipient ANRed and we ended the drag
// out from under it. Log the issue and move on.
@@ -205,24 +219,24 @@ class DragDropController {
// 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);
+ mHandler.removeMessages(MSG_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?
}
mDragState.mDragResult = consumed;
- mDragState.endDragLw();
+ mDragState.endDragLocked();
}
}
- void cancelDragAndDrop(WindowManagerService service, IBinder dragToken) {
+ void cancelDragAndDrop(IBinder dragToken) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "cancelDragAndDrop");
}
- synchronized (service.mWindowMap) {
+ synchronized (mService.mWindowMap) {
if (mDragState == null) {
Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
@@ -236,7 +250,7 @@ class DragDropController {
}
mDragState.mDragResult = false;
- mDragState.cancelDragLw();
+ mDragState.cancelDragLocked();
}
}
@@ -252,49 +266,90 @@ class DragDropController {
}
}
- 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 (mDragState != null) {
- mDragState.unregister();
- mDragState.reset();
- mDragState = null;
+ /**
+ * Sends a message to the Handler managed by DragDropController.
+ */
+ void sendHandlerMessage(int what, Object arg) {
+ mHandler.obtainMessage(what, arg).sendToTarget();
+ }
+
+ /**
+ * Sends a timeout message to the Handler managed by DragDropController.
+ */
+ void sendTimeoutMessage(int what, Object arg) {
+ mHandler.removeMessages(what, arg);
+ final Message msg = mHandler.obtainMessage(what, arg);
+ mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
+ }
+
+ private class DragHandler extends Handler {
+ /**
+ * Lock for window manager.
+ */
+ private final WindowManagerService mService;
+
+ DragHandler(WindowManagerService service, Looper looper) {
+ super(looper);
+ mService = service;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DRAG_START_TIMEOUT: {
+ IBinder win = (IBinder) msg.obj;
+ if (DEBUG_DRAG) {
+ Slog.w(TAG_WM, "Timeout starting drag by win " + win);
}
+ synchronized (mService.mWindowMap) {
+ // !!! TODO: ANR the app that has failed to start the drag in time
+ if (mDragState != null) {
+ mDragState.unregister();
+ mDragState.reset();
+ mDragState = null;
+ }
+ }
+ break;
}
- break;
- }
- case H.DRAG_END_TIMEOUT: {
- IBinder win = (IBinder) msg.obj;
- if (DEBUG_DRAG) {
- Slog.w(TAG_WM, "Timeout ending drag to win " + win);
+ case MSG_DRAG_END_TIMEOUT: {
+ IBinder win = (IBinder) msg.obj;
+ if (DEBUG_DRAG) {
+ Slog.w(TAG_WM, "Timeout ending drag to win " + win);
+ }
+ synchronized (mService.mWindowMap) {
+ // !!! TODO: ANR the drag-receiving app
+ if (mDragState != null) {
+ mDragState.mDragResult = false;
+ mDragState.endDragLocked();
+ }
+ }
+ break;
}
- synchronized (service.mWindowMap) {
- // !!! TODO: ANR the drag-receiving app
- if (mDragState != null) {
- mDragState.mDragResult = false;
- mDragState.endDragLw();
+
+ case MSG_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 (mService.mWindowMap) {
+ interceptor.tearDown();
+ }
}
+ break;
}
- 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();
+ case MSG_ANIMATION_END: {
+ synchronized (mService.mWindowMap) {
+ if (mDragState == null) {
+ Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
+ "plyaing animation");
+ return;
+ }
+ mDragState.onAnimationEndLocked();
}
+ break;
}
- break;
}
}
}
diff --git a/services/core/java/com/android/server/wm/DragInputEventReceiver.java b/services/core/java/com/android/server/wm/DragInputEventReceiver.java
new file mode 100644
index 000000000000..b4bbc9055631
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DragInputEventReceiver.java
@@ -0,0 +1,151 @@
+/*
+ * 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.view.InputDevice.SOURCE_CLASS_POINTER;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.BUTTON_STYLUS_PRIMARY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.os.Looper;
+import android.util.Slog;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+
+/**
+ * Input receiver for drag and drop
+ */
+class DragInputEventReceiver extends InputEventReceiver {
+ private final WindowManagerService mService;
+ private final DragDropController mDragDropController;
+
+ // Set, if stylus button was down at the start of the drag.
+ private boolean mStylusButtonDownAtStart;
+ // Indicates the first event to check for button state.
+ private boolean mIsStartEvent = true;
+ // Set to true to ignore input events after the drag gesture is complete but the drag events
+ // are still being dispatched.
+ private boolean mMuteInput = false;
+
+ public DragInputEventReceiver(InputChannel inputChannel, Looper looper,
+ DragDropController controller, WindowManagerService service) {
+ super(inputChannel, looper);
+ mDragDropController = controller;
+ mService = service;
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event, int displayId) {
+ boolean handled = false;
+ try {
+ synchronized (mService.mWindowMap) {
+ if (!mDragDropController.dragDropActiveLocked()) {
+ // The drag has ended but the clean-up message has not been processed by
+ // window manager. Drop events that occur after this until window manager
+ // has a chance to clean-up the input handle.
+ handled = true;
+ return;
+ }
+ if (!(event instanceof MotionEvent)
+ || (event.getSource() & SOURCE_CLASS_POINTER) == 0
+ || mMuteInput) {
+ return;
+ }
+ final MotionEvent motionEvent = (MotionEvent) event;
+ boolean endDrag = false;
+ final float newX = motionEvent.getRawX();
+ final float newY = motionEvent.getRawY();
+ final boolean isStylusButtonDown =
+ (motionEvent.getButtonState() & BUTTON_STYLUS_PRIMARY) != 0;
+
+ if (mIsStartEvent) {
+ if (isStylusButtonDown) {
+ // First event and the button was down, check for the button being
+ // lifted in the future, if that happens we'll drop the item.
+ mStylusButtonDownAtStart = true;
+ }
+ mIsStartEvent = false;
+ }
+
+ switch (motionEvent.getAction()) {
+ case ACTION_DOWN: {
+ if (DEBUG_DRAG) Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
+ }
+ break;
+
+ case ACTION_MOVE: {
+ if (mStylusButtonDownAtStart && !isStylusButtonDown) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "Button no longer pressed; dropping at "
+ + newX + "," + newY);
+ }
+ mMuteInput = true;
+ endDrag = mDragDropController.mDragState
+ .notifyDropLocked(newX, newY);
+ } else {
+ // move the surface and tell the involved window(s) where we are
+ mDragDropController.mDragState.notifyMoveLocked(newX, newY);
+ }
+ }
+ break;
+
+ case ACTION_UP: {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG_WM, "Got UP on move channel; dropping at "
+ + newX + "," + newY);
+ }
+ mMuteInput = true;
+ endDrag = mDragDropController.mDragState
+ .notifyDropLocked(newX, newY);
+ }
+ break;
+
+ case ACTION_CANCEL: {
+ if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
+ mMuteInput = true;
+ endDrag = true;
+ }
+ break;
+ }
+
+ if (endDrag) {
+ if (DEBUG_DRAG)
+ Slog.d(TAG_WM, "Drag ended; tearing down state");
+ // tell all the windows that the drag has ended
+ // endDragLocked will post back to looper to dispose the receiver
+ // since we still need the receiver for the last finishInputEvent.
+ mDragDropController.mDragState.endDragLocked();
+ mStylusButtonDownAtStart = false;
+ mIsStartEvent = true;
+ }
+
+ handled = true;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG_WM, "Exception caught by drag handleMotion", e);
+ } finally {
+ finishInputEvent(event, handled);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 11f22411d4c5..861fb443b6d0 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static com.android.server.wm.DragDropController.MSG_ANIMATION_END;
+import static com.android.server.wm.DragDropController.MSG_DRAG_END_TIMEOUT;
+import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -29,14 +32,10 @@ import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
-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;
import android.os.ServiceManager;
@@ -50,19 +49,14 @@ 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.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import android.view.animation.Transformation;
+import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
-import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
-import com.android.server.wm.WindowManagerService.H;
-
-import com.android.internal.view.IDragAndDropPermissions;
import java.util.ArrayList;
@@ -86,10 +80,8 @@ class DragState {
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;
+ final DragDropController mDragDropController;
IBinder mToken;
/**
* Do not use the variable from the out of animation thread while mAnimator is not null.
@@ -118,17 +110,16 @@ class DragState {
@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) {
mService = service;
+ mDragDropController = service.mDragDropController;
mToken = token;
mSurfaceControl = surface;
mFlags = flags;
mLocalWin = localWin;
mNotifiedWindows = new ArrayList<WindowState>();
- mHandler = new DragStateHandler(service.mH.getLooper());
}
void reset() {
@@ -159,8 +150,8 @@ class DragState {
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
- mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
- mService.mH.getLooper());
+ mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
+ mService.mH.getLooper(), mDragDropController, mService);
mDragApplicationHandle = new InputApplicationHandle(null);
mDragApplicationHandle.name = "drag";
@@ -171,7 +162,7 @@ class DragState {
display.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.inputChannel = mServerChannel;
- mDragWindowHandle.layer = getDragLayerLw();
+ mDragWindowHandle.layer = getDragLayerLocked();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutNanos =
@@ -250,14 +241,14 @@ class DragState {
Slog.e(TAG_WM, "Unregister of nonexistent drag input channel");
} else {
// Input channel should be disposed on the thread where the input is being handled.
- mService.mH.obtainMessage(
- H.TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor).sendToTarget();
+ mDragDropController.sendHandlerMessage(
+ MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT, mInputInterceptor);
mInputInterceptor = null;
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
}
}
- int getDragLayerLw() {
+ int getDragLayerLocked() {
return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_DRAG)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
@@ -265,7 +256,7 @@ class DragState {
/* call out to each visible window/session informing it about the drag
*/
- void broadcastDragStartedLw(final float touchX, final float touchY) {
+ void broadcastDragStartedLocked(final float touchX, final float touchY) {
mOriginalX = mCurrentX = touchX;
mOriginalY = mCurrentY = touchY;
@@ -292,7 +283,7 @@ class DragState {
}
mDisplayContent.forAllWindows(w -> {
- sendDragStartedLw(w, touchX, touchY, mDataDescription);
+ sendDragStartedLocked(w, touchX, touchY, mDataDescription);
}, false /* traverseTopToBottom */ );
}
@@ -304,7 +295,7 @@ class DragState {
* This method clones the 'event' parameter if it's being delivered to the same
* process, so it's safe for the caller to call recycle() on the event afterwards.
*/
- private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
+ private void sendDragStartedLocked(WindowState newWin, float touchX, float touchY,
ClipDescription desc) {
if (mDragInProgress && isValidDropTarget(newWin)) {
DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
@@ -353,7 +344,7 @@ class DragState {
* previously been notified, i.e. it became visible after the drag operation
* was begun. This is a rare case.
*/
- void sendDragStartedIfNeededLw(WindowState newWin) {
+ void sendDragStartedIfNeededLocked(WindowState newWin) {
if (mDragInProgress) {
// If we have sent the drag-started, we needn't do so again
if (isWindowNotified(newWin)) {
@@ -362,7 +353,7 @@ class DragState {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin);
}
- sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
+ sendDragStartedLocked(newWin, mCurrentX, mCurrentY, mDataDescription);
}
}
@@ -375,7 +366,7 @@ class DragState {
return false;
}
- private void broadcastDragEndedLw() {
+ private void broadcastDragEndedLocked() {
final int myPid = Process.myPid();
if (DEBUG_DRAG) {
@@ -406,7 +397,7 @@ class DragState {
mDragInProgress = false;
}
- void endDragLw() {
+ void endDragLocked() {
if (mAnimator != null) {
return;
}
@@ -414,10 +405,10 @@ class DragState {
mAnimator = createReturnAnimationLocked();
return; // Will call cleanUpDragLw when the animation is done.
}
- cleanUpDragLw();
+ cleanUpDragLocked();
}
- void cancelDragLw() {
+ void cancelDragLocked() {
if (mAnimator != null) {
return;
}
@@ -430,14 +421,14 @@ class DragState {
// WindowManagerService, which will cause DragState#reset() while playing the
// cancel animation.
reset();
- mService.mDragDropController.mDragState = null;
+ mDragDropController.mDragState = null;
return;
}
mAnimator = createCancelAnimationLocked();
}
- private void cleanUpDragLw() {
- broadcastDragEndedLw();
+ private void cleanUpDragLocked() {
+ broadcastDragEndedLocked();
if (isFromSource(InputDevice.SOURCE_MOUSE)) {
mService.restorePointerIconLocked(mDisplayContent, mCurrentX, mCurrentY);
}
@@ -447,10 +438,10 @@ class DragState {
// free our resources and drop all the object references
reset();
- mService.mDragDropController.mDragState = null;
+ mDragDropController.mDragState = null;
}
- void notifyMoveLw(float x, float y) {
+ void notifyMoveLocked(float x, float y) {
if (mAnimator != null) {
return;
}
@@ -459,7 +450,7 @@ class DragState {
// Move the surface to the given touch
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
- TAG_WM, ">>> OPEN TRANSACTION notifyMoveLw");
+ TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked");
mService.openSurfaceTransaction();
try {
mSurfaceControl.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
@@ -467,14 +458,14 @@ class DragState {
+ mSurfaceControl + ": pos=(" +
(int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("notifyMoveLw");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
- TAG_WM, "<<< CLOSE TRANSACTION notifyMoveLw");
+ TAG_WM, "<<< CLOSE TRANSACTION notifyMoveLocked");
}
- notifyLocationLw(x, y);
+ notifyLocationLocked(x, y);
}
- void notifyLocationLw(float x, float y) {
+ void notifyLocationLocked(float x, float y) {
// Tell the affected window
WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
if (touchedWin != null && !isWindowNotified(touchedWin)) {
@@ -519,7 +510,7 @@ class DragState {
// Find the drop target and tell it about the data. Returns 'true' if we can immediately
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
- boolean notifyDropLw(float x, float y) {
+ boolean notifyDropLocked(float x, float y) {
if (mAnimator != null) {
return false;
}
@@ -563,9 +554,7 @@ class DragState {
touchedWin.mClient.dispatchDragEvent(evt);
// 5 second timeout for this window to respond to the drop
- mService.mH.removeMessages(H.DRAG_END_TIMEOUT, token);
- Message msg = mService.mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
- mService.mH.sendMessageDelayed(msg, 5000);
+ mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, token);
} catch (RemoteException e) {
Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin);
return true;
@@ -578,6 +567,15 @@ class DragState {
return false;
}
+ void onAnimationEndLocked() {
+ if (mAnimator == null) {
+ Slog.wtf(TAG_WM, "Unexpected null mAnimator");
+ return;
+ }
+ mAnimator = null;
+ cleanUpDragLocked();
+ }
+
private static DragEvent obtainDragEvent(WindowState win, int action,
float x, float y, Object localState,
ClipDescription description, ClipData data,
@@ -641,40 +639,13 @@ class DragState {
return (mTouchSource & source) == source;
}
- void overridePointerIconLw(int touchSource) {
+ void overridePointerIconLocked(int touchSource) {
mTouchSource = touchSource;
if (isFromSource(InputDevice.SOURCE_MOUSE)) {
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.mDragDropController.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
@@ -708,7 +679,7 @@ class DragState {
public void onAnimationEnd(Animator animator) {
// Updating mDragState requires the WM lock so continues it on the out of
// AnimationThread.
- mHandler.sendEmptyMessage(MSG_ANIMATION_END);
+ mDragDropController.sendHandlerMessage(MSG_ANIMATION_END, null);
}
}
}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 36753b7d5e04..d40db8cb5443 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,21 +16,36 @@
package com.android.server.wm;
+import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.view.Display;
import android.view.InputChannel;
import android.view.WindowManager;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
-class InputConsumerImpl {
+import java.io.PrintWriter;
+
+class InputConsumerImpl implements IBinder.DeathRecipient {
final WindowManagerService mService;
final InputChannel mServerChannel, mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
- InputConsumerImpl(WindowManagerService service, String name, InputChannel inputChannel) {
+ final IBinder mToken;
+ final String mName;
+ final int mClientPid;
+ final UserHandle mClientUser;
+
+ InputConsumerImpl(WindowManagerService service, IBinder token, String name,
+ InputChannel inputChannel, int clientPid, UserHandle clientUser) {
mService = service;
+ mToken = token;
+ mName = name;
+ mClientPid = clientPid;
+ mClientUser = clientUser;
InputChannel[] channels = InputChannel.openInputChannelPair(name);
mServerChannel = channels[0];
@@ -68,6 +83,26 @@ class InputConsumerImpl {
mWindowHandle.scaleFactor = 1.0f;
}
+ void linkToDeathRecipient() {
+ if (mToken == null) {
+ return;
+ }
+
+ try {
+ mToken.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ // Client died, do nothing
+ }
+ }
+
+ void unlinkFromDeathRecipient() {
+ if (mToken == null) {
+ return;
+ }
+
+ mToken.unlinkToDeath(this, 0);
+ }
+
void layout(int dw, int dh) {
mWindowHandle.touchableRegion.set(0, 0, dw, dh);
mWindowHandle.frameLeft = 0;
@@ -86,5 +121,19 @@ class InputConsumerImpl {
mService.mInputManager.unregisterInputChannel(mServerChannel);
mClientChannel.dispose();
mServerChannel.dispose();
+ unlinkFromDeathRecipient();
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mService.getWindowManagerLock()) {
+ // Clean up the input consumer
+ mService.mInputMonitor.destroyInputConsumer(mName);
+ unlinkFromDeathRecipient();
+ }
+ }
+
+ void dump(PrintWriter pw, String name, String prefix) {
+ pw.println(prefix + " name=" + name + " pid=" + mClientPid + " user=" + mClientUser);
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index cf1f1713a611..40eab45a26d9 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLP
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
@@ -34,8 +35,11 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.Debug;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -43,7 +47,6 @@ import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.KeyEvent;
import android.view.WindowManager;
-
import android.view.WindowManagerPolicy;
import com.android.server.input.InputApplicationHandle;
@@ -106,8 +109,9 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor,
Looper looper, String name,
- InputEventReceiver.Factory inputEventReceiverFactory) {
- super(service, name, null);
+ InputEventReceiver.Factory inputEventReceiverFactory,
+ int clientPid, UserHandle clientUser) {
+ super(service, null /* token */, name, null /* inputChannel */, clientPid, clientUser);
mInputMonitor = monitor;
mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
mClientChannel, looper);
@@ -129,6 +133,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
private void addInputConsumer(String name, InputConsumerImpl consumer) {
mInputConsumers.put(name, consumer);
+ consumer.linkToDeathRecipient();
updateInputWindowsLw(true /* force */);
}
@@ -166,17 +171,20 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
}
final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService,
- this, looper, name, inputEventReceiverFactory);
+ this, looper, name, inputEventReceiverFactory, Process.myPid(),
+ UserHandle.SYSTEM);
addInputConsumer(name, consumer);
return consumer;
}
- void createInputConsumer(String name, InputChannel inputChannel) {
+ void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid,
+ UserHandle clientUser) {
if (mInputConsumers.containsKey(name)) {
throw new IllegalStateException("Existing input consumer found with name: " + name);
}
- final InputConsumerImpl consumer = new InputConsumerImpl(mService, name, inputChannel);
+ final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name,
+ inputChannel, clientPid, clientUser);
switch (name) {
case INPUT_CONSUMER_WALLPAPER:
consumer.mWindowHandle.hasWallpaper = true;
@@ -593,7 +601,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
if (!inputConsumerKeys.isEmpty()) {
pw.println(prefix + "InputConsumers:");
for (String key : inputConsumerKeys) {
- pw.println(prefix + " name=" + key);
+ mInputConsumers.get(key).dump(pw, key, prefix);
}
}
}
@@ -690,7 +698,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
// If there's a drag in progress and 'child' is a potential drop target,
// make sure it's been told about the drag
if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
- mService.mDragDropController.mDragState.sendDragStartedIfNeededLw(w);
+ mService.mDragDropController.mDragState.sendDragStartedIfNeededLocked(w);
}
addInputWindowHandle(
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a7104410f4d3..bcb6e6736d04 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -457,7 +457,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
try {
forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("removeReplacedWindows");
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
}
}
@@ -599,7 +599,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index c25b19ccc6ff..3350feae1bc0 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -296,7 +296,7 @@ class ScreenRotationAnimation {
setRotationInTransaction(originalRotation);
} finally {
if (!inTransaction) {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation");
}
@@ -567,7 +567,7 @@ class ScreenRotationAnimation {
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation.startAnimation");
if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
@@ -607,7 +607,7 @@ class ScreenRotationAnimation {
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation.startAnimation");
if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
@@ -629,7 +629,7 @@ class ScreenRotationAnimation {
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation.startAnimation");
if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 717c5774f9f3..ad7c300a03fa 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -313,7 +313,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final long ident = Binder.clearCallingIdentity();
try {
return mDragDropController.prepareDrag(
- mService, mSurfaceSession, callerPid, callerUid, window, flags, width, height,
+ mSurfaceSession, callerPid, callerUid, window, flags, width, height,
outSurface);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -324,7 +324,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
public boolean performDrag(IWindow window, IBinder dragToken,
int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
ClipData data) {
- return mDragDropController.performDrag(mService, window, dragToken, touchSource,
+ return mDragDropController.performDrag(window, dragToken, touchSource,
touchX, touchY, thumbCenterX, thumbCenterY, data);
}
@@ -332,7 +332,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
public void reportDropResult(IWindow window, boolean consumed) {
final long ident = Binder.clearCallingIdentity();
try {
- mDragDropController.reportDropResult(mService, window, consumed);
+ mDragDropController.reportDropResult(window, consumed);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -342,7 +342,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
public void cancelDragAndDrop(IBinder dragToken) {
final long ident = Binder.clearCallingIdentity();
try {
- mDragDropController.cancelDragAndDrop(mService, dragToken);
+ mDragDropController.cancelDragAndDrop(dragToken);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7620cb0dfdb9..13435d76cd07 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -419,7 +419,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return mFillsParent
|| !inSplitScreenSecondaryWindowingMode()
|| displayContent == null
- || displayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() != null;
+ || displayContent.getSplitScreenPrimaryStackIgnoringVisibility() != null;
}
/** Original bounds of the task if applicable, otherwise fullscreen rect. */
@@ -678,7 +678,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
mChildren.get(i).forceWindowsScaleableInTransaction(force);
}
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("forceWindowsScaleable");
}
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 87de1514c053..12f6b5a20b47 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -210,9 +210,9 @@ class TaskPositioner implements DimLayer.DimLayerUser {
if (mCurrentDimSide != CTRL_NONE) {
final int createMode = mCurrentDimSide == CTRL_LEFT
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
- mService.mActivityManager.moveTaskToDockedStack(
+ ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+ : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+ mService.mActivityManager.setTaskWindowingModeSplitScreenPrimary(
mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
null /* initialBounds */);
}
@@ -637,7 +637,7 @@ class TaskPositioner implements DimLayer.DimLayerUser {
} else {
showDimLayer();
}
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("updateDimLayerVisibility");
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 791accf8f347..6e89e3ea37bb 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -294,7 +294,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
if (mFillsParent
|| !inSplitScreenSecondaryWindowingMode()
|| mDisplayContent == null
- || mDisplayContent.getSplitScreenPrimaryStackStack() != null) {
+ || mDisplayContent.getSplitScreenPrimaryStack() != null) {
return true;
}
return false;
@@ -450,8 +450,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
// might change after a rotation and the original values will be invalid.
mService.setDockedStackCreateStateLocked(
(newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+ ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+ : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
null);
mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
}
@@ -523,7 +523,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
mService.mContext.getResources(), displayWidth, displayHeight,
dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
- isMinimizedDockAndHomeStackResizable());
+ getDockSide(), isMinimizedDockAndHomeStackResizable());
final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
// Recalculate the bounds based on the position of the target.
@@ -693,7 +693,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
"animation background stackId=" + mStackId);
Rect bounds = null;
- final TaskStack dockedStack = dc.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility();
if (inSplitScreenPrimaryWindowingMode()
|| (dockedStack != null && inSplitScreenSecondaryWindowingMode()
&& !dockedStack.fillsParent())) {
@@ -708,7 +708,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
dockedStack.getRawBounds(mTmpRect2);
}
final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
- == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(),
dockedOnTopOrLeft);
@@ -773,7 +773,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
final TaskStack dockedStack =
- mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
if (dockedStack == null) {
// Not sure why you are calling this method when there is no docked stack...
throw new IllegalStateException(
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index e409a68f2dfe..1912095caa1d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -233,7 +233,7 @@ public class WindowAnimator {
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("WindowAnimator");
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f31ea67c8edf..bf5f4bc83e6c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -20,7 +20,7 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
@@ -80,7 +80,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIO
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
@@ -137,7 +136,6 @@ import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
-import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -163,7 +161,9 @@ import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -203,14 +203,12 @@ import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
import android.view.InputChannel;
import android.view.InputDevice;
-import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -226,7 +224,7 @@ import android.view.animation.Animation;
import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
-import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -365,6 +363,8 @@ public class WindowManagerService extends IWindowManager.Stub
private static final int TRANSITION_ANIMATION_SCALE = 1;
private static final int ANIMATION_DURATION_SCALE = 2;
+ final WindowTracing mWindowTracing;
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
boolean mKeyguardGoingAway;
// VR Vr2d Display Id.
@@ -560,7 +560,7 @@ public class WindowManagerService extends IWindowManager.Stub
// The root of the device window hierarchy.
RootWindowContainer mRoot;
- int mDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
Rect mDockedStackCreateBounds;
private final SparseIntArray mTmpTaskIds = new SparseIntArray();
@@ -751,7 +751,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mAllowTheaterModeWakeFromLayout;
TaskPositioner mTaskPositioner;
- final DragDropController mDragDropController = new DragDropController();
+ final DragDropController mDragDropController;
// For frozen screen animations.
private int mExitAnimId, mEnterAnimId;
@@ -771,110 +771,6 @@ public class WindowManagerService extends IWindowManager.Stub
private WindowContentFrameStats mTempWindowRenderStats;
- final class DragInputEventReceiver extends InputEventReceiver {
- // Set, if stylus button was down at the start of the drag.
- private boolean mStylusButtonDownAtStart;
- // Indicates the first event to check for button state.
- private boolean mIsStartEvent = true;
- // Set to true to ignore input events after the drag gesture is complete but the drag events
- // are still being dispatched.
- private boolean mMuteInput = false;
-
- public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEvent(InputEvent event, int displayId) {
- boolean handled = false;
- try {
- if (mDragDropController.mDragState == null) {
- // The drag has ended but the clean-up message has not been processed by
- // window manager. Drop events that occur after this until window manager
- // has a chance to clean-up the input handle.
- handled = true;
- return;
- }
- if (event instanceof MotionEvent
- && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
- && !mMuteInput) {
- final MotionEvent motionEvent = (MotionEvent)event;
- boolean endDrag = false;
- final float newX = motionEvent.getRawX();
- final float newY = motionEvent.getRawY();
- final boolean isStylusButtonDown =
- (motionEvent.getButtonState() & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0;
-
- if (mIsStartEvent) {
- if (isStylusButtonDown) {
- // First event and the button was down, check for the button being
- // lifted in the future, if that happens we'll drop the item.
- mStylusButtonDownAtStart = true;
- }
- mIsStartEvent = false;
- }
-
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_DOWN: {
- if (DEBUG_DRAG) {
- Slog.w(TAG_WM, "Unexpected ACTION_DOWN in drag layer");
- }
- } break;
-
- case MotionEvent.ACTION_MOVE: {
- if (mStylusButtonDownAtStart && !isStylusButtonDown) {
- if (DEBUG_DRAG) Slog.d(TAG_WM, "Button no longer pressed; dropping at "
- + newX + "," + newY);
- mMuteInput = true;
- synchronized (mWindowMap) {
- endDrag = mDragDropController.mDragState.notifyDropLw(newX, newY);
- }
- } else {
- synchronized (mWindowMap) {
- // move the surface and tell the involved window(s) where we are
- mDragDropController.mDragState.notifyMoveLw(newX, newY);
- }
- }
- } break;
-
- case MotionEvent.ACTION_UP: {
- if (DEBUG_DRAG) Slog.d(TAG_WM, "Got UP on move channel; dropping at "
- + newX + "," + newY);
- mMuteInput = true;
- synchronized (mWindowMap) {
- endDrag = mDragDropController.mDragState.notifyDropLw(newX, newY);
- }
- } break;
-
- case MotionEvent.ACTION_CANCEL: {
- if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag cancelled!");
- mMuteInput = true;
- endDrag = true;
- } break;
- }
-
- if (endDrag) {
- if (DEBUG_DRAG) Slog.d(TAG_WM, "Drag ended; tearing down state");
- // tell all the windows that the drag has ended
- synchronized (mWindowMap) {
- // endDragLw will post back to looper to dispose the receiver
- // since we still need the receiver for the last finishInputEvent.
- mDragDropController.mDragState.endDragLw();
- }
- mStylusButtonDownAtStart = false;
- mIsStartEvent = true;
- }
-
- handled = true;
- }
- } catch (Exception e) {
- Slog.e(TAG_WM, "Exception caught by drag handleMotion", e);
- } finally {
- finishInputEvent(event, handled);
- }
- }
- }
-
/**
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
@@ -928,15 +824,20 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Closes a surface transaction.
+ * @param where debug string indicating where the transaction originated
*/
- void closeSurfaceTransaction() {
+ void closeSurfaceTransaction(String where) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
synchronized (mWindowMap) {
- if (mRoot.mSurfaceTraceEnabled) {
- mRoot.mRemoteEventTrace.closeSurfaceTransaction();
+ try {
+ traceStateLocked(where);
+ } finally {
+ if (mRoot.mSurfaceTraceEnabled) {
+ mRoot.mRemoteEventTrace.closeSurfaceTransaction();
+ }
+ SurfaceControl.closeTransaction();
}
- SurfaceControl.closeTransaction();
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -1037,6 +938,12 @@ public class WindowManagerService extends IWindowManager.Stub
}, 0);
}
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver result) {
+ new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
+ }
+
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
@@ -1067,6 +974,8 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy = policy;
mTaskSnapshotController = new TaskSnapshotController(this);
+ mWindowTracing = WindowTracing.createDefaultAndStartLooper(context);
+
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
if(mInputManager != null) {
@@ -1164,6 +1073,7 @@ public class WindowManagerService extends IWindowManager.Stub
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
+ mDragDropController = new DragDropController(this, mH.getLooper());
LocalServices.addService(WindowManagerInternal.class, new LocalService());
initPolicy();
@@ -1175,7 +1085,7 @@ public class WindowManagerService extends IWindowManager.Stub
try {
createWatermarkInTransaction();
} finally {
- closeSurfaceTransaction();
+ closeSurfaceTransaction("createWatermarkInTransaction");
}
showEmulatorDisplayOverlayIfNeeded();
@@ -3391,7 +3301,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Notify whether the docked stack exists for the current user
final DisplayContent displayContent = getDefaultDisplayContentLocked();
final TaskStack stack =
- displayContent.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ displayContent.getSplitScreenPrimaryStackIgnoringVisibility();
displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
stack != null && stack.hasTaskForUser(newUserId));
@@ -3679,7 +3589,7 @@ public class WindowManagerService extends IWindowManager.Stub
mCircularDisplayMask = null;
}
} finally {
- closeSurfaceTransaction();
+ closeSurfaceTransaction("showCircularMask");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
"<<< CLOSE TRANSACTION showCircularMask(visible=" + visible + ")");
}
@@ -3704,7 +3614,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
mEmulatorDisplayOverlay.setVisibility(true);
} finally {
- closeSurfaceTransaction();
+ closeSurfaceTransaction("showEmulatorDisplayOverlay");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
"<<< CLOSE TRANSACTION showEmulatorDisplayOverlay");
}
@@ -3783,7 +3693,7 @@ public class WindowManagerService extends IWindowManager.Stub
* of the target image.
*/
@Override
- public boolean requestAssistScreenshot(final IAssistScreenshotReceiver receiver) {
+ public boolean requestAssistScreenshot(final IAssistDataReceiver receiver) {
if (!checkCallingPermission(READ_FRAME_BUFFER,
"requestAssistScreenshot()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
@@ -3795,7 +3705,7 @@ public class WindowManagerService extends IWindowManager.Stub
1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */,
false /* includeDecor */);
try {
- receiver.send(bm);
+ receiver.onHandleAssistScreenshot(bm);
} catch (RemoteException e) {
}
});
@@ -4796,8 +4706,6 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int APP_FREEZE_TIMEOUT = 17;
public static final int SEND_NEW_CONFIGURATION = 18;
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;
@@ -4824,8 +4732,6 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
- public static final int TEAR_DOWN_DRAG_AND_DROP_INPUT = 44;
-
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
@@ -5053,13 +4959,6 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
- case DRAG_START_TIMEOUT:
- case DRAG_END_TIMEOUT:
- case TEAR_DOWN_DRAG_AND_DROP_INPUT: {
- mDragDropController.handleMessage(WindowManagerService.this, msg);
- break;
- }
-
case REPORT_HARD_KEYBOARD_STATUS_CHANGE: {
notifyHardKeyboardStatusChange();
break;
@@ -6253,9 +6152,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void createInputConsumer(String name, InputChannel inputChannel) {
+ public void createInputConsumer(IBinder token, String name, InputChannel inputChannel) {
synchronized (mWindowMap) {
- mInputMonitor.createInputConsumer(name, inputChannel);
+ mInputMonitor.createInputConsumer(token, name, inputChannel, Binder.getCallingPid(),
+ Binder.getCallingUserHandle());
}
}
@@ -6437,7 +6337,7 @@ public class WindowManagerService extends IWindowManager.Stub
* @param proto Stream to write the WindowContainer object to.
* @param trim If true, reduce the amount of data written.
*/
- private void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
+ void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
mPolicy.writeToProto(proto, POLICY);
mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER, trim);
if (mCurrentFocus != null) {
@@ -6456,6 +6356,17 @@ public class WindowManagerService extends IWindowManager.Stub
mAppTransition.writeToProto(proto, APP_TRANSITION);
}
+ void traceStateLocked(String where) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
+ try {
+ mWindowTracing.traceStateLocked(where, this);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Exception while tracing state", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
@@ -7024,7 +6935,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int getDockedStackSide() {
synchronized (mWindowMap) {
final TaskStack dockedStack = getDefaultDisplayContentLocked()
- .getSplitScreenPrimaryStackStackIgnoringVisibility();
+ .getSplitScreenPrimaryStackIgnoringVisibility();
return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
new file mode 100644
index 000000000000..4b98d9d9921d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * ShellCommands for WindowManagerService.
+ *
+ * Use with {@code adb shell cmd window ...}.
+ */
+public class WindowManagerShellCommand extends ShellCommand {
+
+ private final WindowManagerService mService;
+
+ public WindowManagerShellCommand(WindowManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ switch (cmd) {
+ case "tracing":
+ return mService.mWindowTracing.onShellCommand(this, getNextArgRequired());
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Window Manager (window) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" tracing (start | stop)");
+ pw.println(" start or stop window tracing");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4370a7637e01..6b1932d7b6ae 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1849,110 +1849,109 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final long origId = Binder.clearCallingIdentity();
- disposeInputChannel();
+ try {
+ disposeInputChannel();
+
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
+ + ": mSurfaceController=" + mWinAnimator.mSurfaceController
+ + " mAnimatingExit=" + mAnimatingExit
+ + " mRemoveOnExit=" + mRemoveOnExit
+ + " mHasSurface=" + mHasSurface
+ + " surfaceShowing=" + mWinAnimator.getShown()
+ + " isAnimationSet=" + mWinAnimator.isAnimationSet()
+ + " app-animation="
+ + (mAppToken != null ? mAppToken.mAppAnimator.animation : null)
+ + " mWillReplaceWindow=" + mWillReplaceWindow
+ + " inPendingTransaction="
+ + (mAppToken != null ? mAppToken.inPendingTransaction : false)
+ + " mDisplayFrozen=" + mService.mDisplayFrozen
+ + " callers=" + Debug.getCallers(6));
+
+ // Visibility of the removed window. Will be used later to update orientation later on.
+ boolean wasVisible = false;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
- + ": mSurfaceController=" + mWinAnimator.mSurfaceController
- + " mAnimatingExit=" + mAnimatingExit
- + " mRemoveOnExit=" + mRemoveOnExit
- + " mHasSurface=" + mHasSurface
- + " surfaceShowing=" + mWinAnimator.getShown()
- + " isAnimationSet=" + mWinAnimator.isAnimationSet()
- + " app-animation="
- + (mAppToken != null ? mAppToken.mAppAnimator.animation : null)
- + " mWillReplaceWindow=" + mWillReplaceWindow
- + " inPendingTransaction="
- + (mAppToken != null ? mAppToken.inPendingTransaction : false)
- + " mDisplayFrozen=" + mService.mDisplayFrozen
- + " callers=" + Debug.getCallers(6));
-
- // Visibility of the removed window. Will be used later to update orientation later on.
- boolean wasVisible = false;
-
- final int displayId = getDisplayId();
-
- // First, see if we need to run an animation. If we do, we have to hold off on removing the
- // window until the animation is done. If the display is frozen, just remove immediately,
- // since the animation wouldn't be seen.
- if (mHasSurface && mToken.okToAnimate()) {
- if (mWillReplaceWindow) {
- // This window is going to be replaced. We need to keep it around until the new one
- // gets added, then we will get rid of this one.
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Preserving " + this + " until the new one is " + "added");
- // TODO: We are overloading mAnimatingExit flag to prevent the window state from
- // been removed. We probably need another flag to indicate that window removal
- // should be deffered vs. overloading the flag that says we are playing an exit
- // animation.
- mAnimatingExit = true;
- mReplacingRemoveRequested = true;
- Binder.restoreCallingIdentity(origId);
- return;
- }
+ final int displayId = getDisplayId();
- // If we are not currently running the exit animation, we need to see about starting one
- wasVisible = isWinVisibleLw();
+ // First, see if we need to run an animation. If we do, we have to hold off on removing the
+ // window until the animation is done. If the display is frozen, just remove immediately,
+ // since the animation wouldn't be seen.
+ if (mHasSurface && mToken.okToAnimate()) {
+ if (mWillReplaceWindow) {
+ // This window is going to be replaced. We need to keep it around until the new one
+ // gets added, then we will get rid of this one.
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ "Preserving " + this + " until the new one is " + "added");
+ // TODO: We are overloading mAnimatingExit flag to prevent the window state from
+ // been removed. We probably need another flag to indicate that window removal
+ // should be deffered vs. overloading the flag that says we are playing an exit
+ // animation.
+ mAnimatingExit = true;
+ mReplacingRemoveRequested = true;
+ return;
+ }
- if (keepVisibleDeadWindow) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Not removing " + this + " because app died while it's visible");
+ // If we are not currently running the exit animation, we need to see about starting one
+ wasVisible = isWinVisibleLw();
- mAppDied = true;
- setDisplayLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
+ if (keepVisibleDeadWindow) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ "Not removing " + this + " because app died while it's visible");
- // Set up a replacement input channel since the app is now dead.
- // We need to catch tapping on the dead window to restart the app.
- openInputChannel(null);
- mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+ mAppDied = true;
+ setDisplayLayoutNeeded();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
- Binder.restoreCallingIdentity(origId);
- return;
- }
+ // Set up a replacement input channel since the app is now dead.
+ // We need to catch tapping on the dead window to restart the app.
+ openInputChannel(null);
+ mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+ return;
+ }
- if (wasVisible) {
- final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
+ if (wasVisible) {
+ final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
- // Try starting an animation.
- if (mWinAnimator.applyAnimationLocked(transit, false)) {
- mAnimatingExit = true;
- }
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+ // Try starting an animation.
+ if (mWinAnimator.applyAnimationLocked(transit, false)) {
+ mAnimatingExit = true;
+ }
+ //TODO (multidisplay): Magnification is supported only for the default display.
+ if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+ }
}
- }
- final boolean isAnimating =
- mWinAnimator.isAnimationSet() && !mWinAnimator.isDummyAnimation();
- final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
- && mAppToken.isLastWindow(this);
- // We delay the removal of a window if it has a showing surface that can be used to run
- // exit animation and it is marked as exiting.
- // Also, If isn't the an animating starting window that is the last window in the app.
- // We allow the removal of the non-animating starting window now as there is no
- // additional window or animation that will trigger its removal.
- if (mWinAnimator.getShown() && mAnimatingExit
- && (!lastWindowIsStartingWindow || isAnimating)) {
- // The exit animation is running or should run... wait for it!
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Not removing " + this + " due to exit animation ");
- setupWindowForRemoveOnExit();
- if (mAppToken != null) {
- mAppToken.updateReportedVisibilityLocked();
+ final boolean isAnimating =
+ mWinAnimator.isAnimationSet() && !mWinAnimator.isDummyAnimation();
+ final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
+ && mAppToken.isLastWindow(this);
+ // We delay the removal of a window if it has a showing surface that can be used to run
+ // exit animation and it is marked as exiting.
+ // Also, If isn't the an animating starting window that is the last window in the app.
+ // We allow the removal of the non-animating starting window now as there is no
+ // additional window or animation that will trigger its removal.
+ if (mWinAnimator.getShown() && mAnimatingExit
+ && (!lastWindowIsStartingWindow || isAnimating)) {
+ // The exit animation is running or should run... wait for it!
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ "Not removing " + this + " due to exit animation ");
+ setupWindowForRemoveOnExit();
+ if (mAppToken != null) {
+ mAppToken.updateReportedVisibilityLocked();
+ }
+ return;
}
- Binder.restoreCallingIdentity(origId);
- return;
}
- }
- removeImmediately();
- // Removing a visible window will effect the computed orientation
- // So just update orientation if needed.
- if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+ removeImmediately();
+ // Removing a visible window will effect the computed orientation
+ // So just update orientation if needed.
+ if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+ }
+ mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- Binder.restoreCallingIdentity(origId);
}
private void setupWindowForRemoveOnExit() {
@@ -2361,7 +2360,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// also reset drag resizing state, because the owner can't do it
// anymore.
final TaskStack stack =
- dc.getSplitScreenPrimaryStackStackIgnoringVisibility();
+ dc.getSplitScreenPrimaryStackIgnoringVisibility();
if (stack != null) {
stack.resetDockedStackToMiddle();
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5266903185d4..86397aea6a17 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -732,7 +732,7 @@ class WindowStateAnimator {
mSurfaceController.setLayerStackInTransaction(getLayerStack());
mSurfaceController.setLayer(mAnimLayer);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("createSurfaceLocked");
}
mLastHidden = true;
@@ -1711,7 +1711,7 @@ class WindowStateAnimator {
Slog.w(TAG, "Error positioning surface of " + mWin
+ " pos=(" + left + "," + top + ")", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setWallpaperOffset");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setWallpaperOffset");
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index edd650a42107..a2145230f956 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -259,7 +259,7 @@ class WindowSurfaceController {
mSurfaceControl.setLayer(layer);
}
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setLayer");
}
}
}
@@ -385,7 +385,7 @@ class WindowSurfaceController {
try {
mSurfaceControl.setTransparentRegionHint(region);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setTransparentRegion");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setTransparentRegion");
}
@@ -403,7 +403,7 @@ class WindowSurfaceController {
try {
mSurfaceControl.setOpaque(isOpaque);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setOpaqueLocked");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
}
}
@@ -420,7 +420,7 @@ class WindowSurfaceController {
try {
mSurfaceControl.setSecure(isSecure);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setSecure");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index d57fdd26d250..cd5e4750554c 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -429,7 +429,7 @@ class WindowSurfacePlacer {
try {
mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("handleAppTransitionReadyLocked");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
new file mode 100644
index 000000000000..5657f6c4f9c5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.ENTRY;
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.wm.proto.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.wm.proto.WindowManagerTraceProto.WHERE;
+import static com.android.server.wm.proto.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
+
+import android.content.Context;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * A class that allows window manager to dump its state continuously to a trace file, such that a
+ * time series of window manager state can be analyzed after the fact.
+ */
+class WindowTracing {
+
+ private static final String TAG = "WindowTracing";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final Object mLock = new Object();
+ private final File mTraceFile;
+ private final BlockingQueue<ProtoOutputStream> mWriteQueue = new ArrayBlockingQueue<>(200);
+
+ private boolean mEnabled;
+ private volatile boolean mEnabledLockFree;
+
+ WindowTracing(File file) {
+ mTraceFile = file;
+ }
+
+ void startTrace(PrintWriter pw) throws IOException {
+ synchronized (mLock) {
+ logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+ mWriteQueue.clear();
+ mTraceFile.delete();
+ try (OutputStream os = new FileOutputStream(mTraceFile)) {
+ ProtoOutputStream proto = new ProtoOutputStream(os);
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ proto.flush();
+ }
+ mEnabled = mEnabledLockFree = true;
+ }
+ }
+
+ private void logAndPrintln(PrintWriter pw, String msg) {
+ Log.i(TAG, msg);
+ pw.println(msg);
+ pw.flush();
+ }
+
+ void stopTrace(PrintWriter pw) {
+ synchronized (mLock) {
+ logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
+ mEnabled = mEnabledLockFree = false;
+ while (!mWriteQueue.isEmpty()) {
+ if (mEnabled) {
+ logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
+ throw new IllegalStateException("tracing enabled while waiting for flush.");
+ }
+ try {
+ mLock.wait();
+ mLock.notify();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+ }
+ }
+
+ void appendTraceEntry(ProtoOutputStream proto) {
+ if (!mEnabledLockFree) {
+ return;
+ }
+
+ if (!mWriteQueue.offer(proto)) {
+ Log.e(TAG, "Dropping window trace entry, queue full");
+ }
+ }
+
+ void loop() {
+ for (;;) {
+ loopOnce();
+ }
+ }
+
+ @VisibleForTesting
+ void loopOnce() {
+ ProtoOutputStream proto;
+ try {
+ proto = mWriteQueue.take();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
+ try (OutputStream os = new FileOutputStream(mTraceFile, true /* append */)) {
+ os.write(proto.getBytes());
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write file " + mTraceFile, e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ mLock.notify();
+ }
+ }
+
+ boolean isEnabled() {
+ return mEnabledLockFree;
+ }
+
+ static WindowTracing createDefaultAndStartLooper(Context context) {
+ File file = new File("/data/system/window_trace.proto");
+ WindowTracing windowTracing = new WindowTracing(file);
+ new Thread(windowTracing::loop, "window_tracing").start();
+ return windowTracing;
+ }
+
+ int onShellCommand(ShellCommand shell, String cmd) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "start":
+ startTrace(pw);
+ return 0;
+ case "stop":
+ stopTrace(pw);
+ return 0;
+ default:
+ pw.println("Unknown command: " + cmd);
+ return -1;
+ }
+ } catch (IOException e) {
+ logAndPrintln(pw, e.toString());
+ throw new RuntimeException(e);
+ }
+ }
+
+ void traceStateLocked(String where, WindowManagerService service) {
+ if (!isEnabled()) {
+ return;
+ }
+ ProtoOutputStream os = new ProtoOutputStream();
+ long tokenOuter = os.start(ENTRY);
+ os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+ os.write(WHERE, where);
+
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
+ try {
+ long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+ service.writeToProtoLocked(os, true /* trim */);
+ os.end(tokenInner);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ os.end(tokenOuter);
+ appendTraceEntry(os);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+}
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 3901cebcc787..d4ffa70417e1 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -48,8 +48,6 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /
status_t err;
- configureRpcThreadpool(5, false /* callerWillJoin */);
-
JavaVM *vm;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Cannot get Java VM");
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index e8ef1686e1b4..2f45181e6f5c 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -92,7 +92,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_tv_TvUinputBridge(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
- register_android_server_Watchdog(env);
register_android_server_HardwarePropertiesManagerService(env);
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
diff --git a/services/coverage/java/com/android/server/coverage/CoverageService.java b/services/coverage/java/com/android/server/coverage/CoverageService.java
index d600aa8c4276..ed8d24aa3b8f 100644
--- a/services/coverage/java/com/android/server/coverage/CoverageService.java
+++ b/services/coverage/java/com/android/server/coverage/CoverageService.java
@@ -112,7 +112,7 @@ public class CoverageService extends Binder {
}
// Try to open the destination file
- ParcelFileDescriptor fd = openOutputFileForSystem(dest);
+ ParcelFileDescriptor fd = openFileForSystem(dest, "w");
if (fd == null) {
return -1;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de5d879a05af..a2c2aeb0fa78 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -545,11 +545,9 @@ public final class SystemServer {
traceEnd();
// Bring up recovery system in case a rescue party needs a reboot
- if (!SystemProperties.getBoolean("config.disable_noncore", false)) {
- traceBeginAndSlog("StartRecoverySystemService");
- mSystemServiceManager.startService(RecoverySystemService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartRecoverySystemService");
+ mSystemServiceManager.startService(RecoverySystemService.class);
+ traceEnd();
// Now that we have the bare essentials of the OS up and running, take
// note that we just booted, which might send out a rescue party if
@@ -660,6 +658,9 @@ public final class SystemServer {
mSystemServiceManager.startService(DropBoxManagerService.class);
traceEnd();
+ // First hwbinder call is in BatteryService.
+ android.os.HwBinder.startRpcThreadPool(5, false /* callerWillJoin */);
+
traceBeginAndSlog("StartBatteryService");
// Tracks the battery level. Requires LightService.
mSystemServiceManager.startService(BatteryService.class);
@@ -705,13 +706,7 @@ public final class SystemServer {
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
- boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
- boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
- boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
- boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
- boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
- boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false);
boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
boolean disableMediaProjection = SystemProperties.getBoolean("config.disable_mediaproj",
false);
@@ -875,8 +870,6 @@ public final class SystemServer {
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
- } else if (disableBluetooth) {
- Slog.i(TAG, "Bluetooth Service disabled by config");
} else {
traceBeginAndSlog("StartBluetoothService");
mSystemServiceManager.startService(BluetoothService.class);
@@ -927,8 +920,7 @@ public final class SystemServer {
traceEnd();
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
- if (!disableStorage &&
- !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+ if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
traceBeginAndSlog("StartStorageManagerService");
try {
/*
@@ -978,42 +970,40 @@ public final class SystemServer {
traceEnd();
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartLockSettingsService");
- try {
- mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS);
- lockSettings = ILockSettings.Stub.asInterface(
- ServiceManager.getService("lock_settings"));
- } catch (Throwable e) {
- reportWtf("starting LockSettingsService service", e);
- }
- traceEnd();
-
- final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
- if (hasPdb) {
- traceBeginAndSlog("StartPersistentDataBlock");
- mSystemServiceManager.startService(PersistentDataBlockService.class);
- traceEnd();
- }
-
- if (hasPdb || OemLockService.isHalPresent()) {
- // Implementation depends on pdb or the OemLock HAL
- traceBeginAndSlog("StartOemLockService");
- mSystemServiceManager.startService(OemLockService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartLockSettingsService");
+ try {
+ mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS);
+ lockSettings = ILockSettings.Stub.asInterface(
+ ServiceManager.getService("lock_settings"));
+ } catch (Throwable e) {
+ reportWtf("starting LockSettingsService service", e);
+ }
+ traceEnd();
- traceBeginAndSlog("StartDeviceIdleController");
- mSystemServiceManager.startService(DeviceIdleController.class);
+ final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
+ if (hasPdb) {
+ traceBeginAndSlog("StartPersistentDataBlock");
+ mSystemServiceManager.startService(PersistentDataBlockService.class);
traceEnd();
+ }
- // Always start the Device Policy Manager, so that the API is compatible with
- // API8.
- traceBeginAndSlog("StartDevicePolicyManager");
- mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
+ if (hasPdb || OemLockService.isHalPresent()) {
+ // Implementation depends on pdb or the OemLock HAL
+ traceBeginAndSlog("StartOemLockService");
+ mSystemServiceManager.startService(OemLockService.class);
traceEnd();
}
+ traceBeginAndSlog("StartDeviceIdleController");
+ mSystemServiceManager.startService(DeviceIdleController.class);
+ traceEnd();
+
+ // Always start the Device Policy Manager, so that the API is compatible with
+ // API8.
+ traceBeginAndSlog("StartDevicePolicyManager");
+ mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
+ traceEnd();
+
if (!disableSystemUI) {
traceBeginAndSlog("StartStatusBarManagerService");
try {
@@ -1025,154 +1015,146 @@ public final class SystemServer {
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartClipboardService");
- mSystemServiceManager.startService(ClipboardService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartClipboardService");
+ mSystemServiceManager.startService(ClipboardService.class);
+ traceEnd();
- if (!disableNetwork) {
- traceBeginAndSlog("StartNetworkManagementService");
- try {
- networkManagement = NetworkManagementService.create(context);
- ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
- } catch (Throwable e) {
- reportWtf("starting NetworkManagement Service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartNetworkManagementService");
+ try {
+ networkManagement = NetworkManagementService.create(context);
+ ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkManagement Service", e);
+ }
+ traceEnd();
- traceBeginAndSlog("StartIpSecService");
- try {
- ipSecService = IpSecService.create(context);
- ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
- } catch (Throwable e) {
- reportWtf("starting IpSec Service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartIpSecService");
+ try {
+ ipSecService = IpSecService.create(context);
+ ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
+ } catch (Throwable e) {
+ reportWtf("starting IpSec Service", e);
}
+ traceEnd();
- if (!disableNonCoreServices && !disableTextServices) {
+ if (!disableTextServices) {
traceBeginAndSlog("StartTextServicesManager");
mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
traceEnd();
}
- if (!disableNetwork) {
- traceBeginAndSlog("StartNetworkScoreService");
- try {
- networkScore = new NetworkScoreService(context);
- ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
- } catch (Throwable e) {
- reportWtf("starting Network Score Service", e);
- }
- traceEnd();
-
- traceBeginAndSlog("StartNetworkStatsService");
- try {
- networkStats = NetworkStatsService.create(context, networkManagement);
- ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
- } catch (Throwable e) {
- reportWtf("starting NetworkStats Service", e);
- }
- traceEnd();
-
- traceBeginAndSlog("StartNetworkPolicyManagerService");
- try {
- networkPolicy = new NetworkPolicyManagerService(context,
- mActivityManagerService, networkStats, networkManagement);
- ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
- } catch (Throwable e) {
- reportWtf("starting NetworkPolicy Service", e);
- }
- traceEnd();
-
- // Wifi Service must be started first for wifi-related services.
- traceBeginAndSlog("StartWifi");
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- traceEnd();
- traceBeginAndSlog("StartWifiScanning");
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
- traceEnd();
+ traceBeginAndSlog("StartNetworkScoreService");
+ try {
+ networkScore = new NetworkScoreService(context);
+ ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
+ } catch (Throwable e) {
+ reportWtf("starting Network Score Service", e);
+ }
+ traceEnd();
- if (!disableRtt) {
- traceBeginAndSlog("StartWifiRtt");
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
- traceEnd();
+ traceBeginAndSlog("StartNetworkStatsService");
+ try {
+ networkStats = NetworkStatsService.create(context, networkManagement);
+ ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkStats Service", e);
+ }
+ traceEnd();
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)) {
- traceBeginAndSlog("StartRttService");
- mSystemServiceManager.startService(
- "com.android.server.wifi.rtt.RttService");
- traceEnd();
- }
- }
+ traceBeginAndSlog("StartNetworkPolicyManagerService");
+ try {
+ networkPolicy = new NetworkPolicyManagerService(context,
+ mActivityManagerService, networkStats, networkManagement);
+ ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkPolicy Service", e);
+ }
+ traceEnd();
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_AWARE)) {
- traceBeginAndSlog("StartWifiAware");
- mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
- traceEnd();
- }
+ // Wifi Service must be started first for wifi-related services.
+ traceBeginAndSlog("StartWifi");
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ traceEnd();
+ traceBeginAndSlog("StartWifiScanning");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.scanner.WifiScanningService");
+ traceEnd();
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_DIRECT)) {
- traceBeginAndSlog("StartWifiP2P");
- mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- traceEnd();
- }
+ if (!disableRtt) {
+ traceBeginAndSlog("StartWifiRtt");
+ mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ traceEnd();
if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_LOWPAN)) {
- traceBeginAndSlog("StartLowpan");
- mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
traceEnd();
}
+ }
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
- traceBeginAndSlog("StartEthernet");
- mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_AWARE)) {
+ traceBeginAndSlog("StartWifiAware");
+ mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
+ traceEnd();
+ }
- traceBeginAndSlog("StartConnectivityService");
- try {
- connectivity = new ConnectivityService(
- context, networkManagement, networkStats, networkPolicy);
- ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
- /* allowIsolated= */ false,
- DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
- networkStats.bindConnectivityManager(connectivity);
- networkPolicy.bindConnectivityManager(connectivity);
- } catch (Throwable e) {
- reportWtf("starting Connectivity Service", e);
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_DIRECT)) {
+ traceBeginAndSlog("StartWifiP2P");
+ mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
traceEnd();
+ }
- traceBeginAndSlog("StartNsdService");
- try {
- serviceDiscovery = NsdService.create(context);
- ServiceManager.addService(
- Context.NSD_SERVICE, serviceDiscovery);
- } catch (Throwable e) {
- reportWtf("starting Service Discovery Service", e);
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LOWPAN)) {
+ traceBeginAndSlog("StartLowpan");
+ mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartUpdateLockService");
- try {
- ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
- new UpdateLockService(context));
- } catch (Throwable e) {
- reportWtf("starting UpdateLockService", e);
- }
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
+ traceBeginAndSlog("StartEthernet");
+ mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
traceEnd();
}
+ traceBeginAndSlog("StartConnectivityService");
+ try {
+ connectivity = new ConnectivityService(
+ context, networkManagement, networkStats, networkPolicy);
+ ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
+ /* allowIsolated= */ false,
+ DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+ networkStats.bindConnectivityManager(connectivity);
+ networkPolicy.bindConnectivityManager(connectivity);
+ } catch (Throwable e) {
+ reportWtf("starting Connectivity Service", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartNsdService");
+ try {
+ serviceDiscovery = NsdService.create(context);
+ ServiceManager.addService(
+ Context.NSD_SERVICE, serviceDiscovery);
+ } catch (Throwable e) {
+ reportWtf("starting Service Discovery Service", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartUpdateLockService");
+ try {
+ ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
+ new UpdateLockService(context));
+ } catch (Throwable e) {
+ reportWtf("starting UpdateLockService", e);
+ }
+ traceEnd();
+
traceBeginAndSlog("StartNotificationManager");
mSystemServiceManager.startService(NotificationManagerService.class);
SystemNotificationChannels.createAll(context);
@@ -1185,27 +1167,25 @@ public final class SystemServer {
mSystemServiceManager.startService(DeviceStorageMonitorService.class);
traceEnd();
- if (!disableLocation) {
- traceBeginAndSlog("StartLocationManagerService");
- try {
- location = new LocationManagerService(context);
- ServiceManager.addService(Context.LOCATION_SERVICE, location);
- } catch (Throwable e) {
- reportWtf("starting Location Manager", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartLocationManagerService");
+ try {
+ location = new LocationManagerService(context);
+ ServiceManager.addService(Context.LOCATION_SERVICE, location);
+ } catch (Throwable e) {
+ reportWtf("starting Location Manager", e);
+ }
+ traceEnd();
- traceBeginAndSlog("StartCountryDetectorService");
- try {
- countryDetector = new CountryDetectorService(context);
- ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
- } catch (Throwable e) {
- reportWtf("starting Country Detector", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartCountryDetectorService");
+ try {
+ countryDetector = new CountryDetectorService(context);
+ ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+ } catch (Throwable e) {
+ reportWtf("starting Country Detector", e);
}
+ traceEnd();
- if (!disableNonCoreServices && !disableSearchManager) {
+ if (!disableSearchManager) {
traceBeginAndSlog("StartSearchManagerService");
try {
mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS);
@@ -1215,8 +1195,7 @@ public final class SystemServer {
traceEnd();
}
- if (!disableNonCoreServices && context.getResources().getBoolean(
- R.bool.config_enableWallpaperService)) {
+ if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) {
traceBeginAndSlog("StartWallpaperManagerService");
mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
traceEnd();
@@ -1232,16 +1211,14 @@ public final class SystemServer {
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartDockObserver");
- mSystemServiceManager.startService(DockObserver.class);
- traceEnd();
+ traceBeginAndSlog("StartDockObserver");
+ mSystemServiceManager.startService(DockObserver.class);
+ traceEnd();
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- traceBeginAndSlog("StartThermalObserver");
- mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ traceBeginAndSlog("StartThermalObserver");
+ mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
+ traceEnd();
}
traceBeginAndSlog("StartWiredAccessoryManager");
@@ -1254,46 +1231,44 @@ public final class SystemServer {
}
traceEnd();
- if (!disableNonCoreServices) {
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
- // Start MIDI Manager service
- traceBeginAndSlog("StartMidiManager");
- mSystemServiceManager.startService(MIDI_SERVICE_CLASS);
- traceEnd();
- }
-
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
- || mPackageManager.hasSystemFeature(
- PackageManager.FEATURE_USB_ACCESSORY)) {
- // Manage USB host and device support
- traceBeginAndSlog("StartUsbService");
- mSystemServiceManager.startService(USB_SERVICE_CLASS);
- traceEnd();
- }
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
+ // Start MIDI Manager service
+ traceBeginAndSlog("StartMidiManager");
+ mSystemServiceManager.startService(MIDI_SERVICE_CLASS);
+ traceEnd();
+ }
- if (!disableSerial) {
- traceBeginAndSlog("StartSerialService");
- try {
- // Serial port support
- serial = new SerialService(context);
- ServiceManager.addService(Context.SERIAL_SERVICE, serial);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting SerialService", e);
- }
- traceEnd();
- }
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
+ || mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_USB_ACCESSORY)) {
+ // Manage USB host and device support
+ traceBeginAndSlog("StartUsbService");
+ mSystemServiceManager.startService(USB_SERVICE_CLASS);
+ traceEnd();
+ }
- traceBeginAndSlog("StartHardwarePropertiesManagerService");
+ if (!disableSerial) {
+ traceBeginAndSlog("StartSerialService");
try {
- hardwarePropertiesService = new HardwarePropertiesManagerService(context);
- ServiceManager.addService(Context.HARDWARE_PROPERTIES_SERVICE,
- hardwarePropertiesService);
+ // Serial port support
+ serial = new SerialService(context);
+ ServiceManager.addService(Context.SERIAL_SERVICE, serial);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting HardwarePropertiesManagerService", e);
+ Slog.e(TAG, "Failure starting SerialService", e);
}
traceEnd();
}
+ traceBeginAndSlog("StartHardwarePropertiesManagerService");
+ try {
+ hardwarePropertiesService = new HardwarePropertiesManagerService(context);
+ ServiceManager.addService(Context.HARDWARE_PROPERTIES_SERVICE,
+ hardwarePropertiesService);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting HardwarePropertiesManagerService", e);
+ }
+ traceEnd();
+
traceBeginAndSlog("StartTwilightService");
mSystemServiceManager.startService(TwilightService.class);
traceEnd();
@@ -1312,47 +1287,45 @@ public final class SystemServer {
mSystemServiceManager.startService(SoundTriggerService.class);
traceEnd();
- if (!disableNonCoreServices) {
- if (!disableTrustManager) {
- traceBeginAndSlog("StartTrustManager");
- mSystemServiceManager.startService(TrustManagerService.class);
- traceEnd();
- }
-
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
- traceBeginAndSlog("StartBackupManager");
- mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
- traceEnd();
- }
-
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
- || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
- traceBeginAndSlog("StartAppWidgerService");
- mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
- traceEnd();
- }
+ if (!disableTrustManager) {
+ traceBeginAndSlog("StartTrustManager");
+ mSystemServiceManager.startService(TrustManagerService.class);
+ traceEnd();
+ }
- // We need to always start this service, regardless of whether the
- // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
- // of initializing various settings. It will internally modify its behavior
- // based on that feature.
- traceBeginAndSlog("StartVoiceRecognitionManager");
- mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
+ traceBeginAndSlog("StartBackupManager");
+ mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
traceEnd();
+ }
- if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
- traceBeginAndSlog("StartGestureLauncher");
- mSystemServiceManager.startService(GestureLauncherService.class);
- traceEnd();
- }
- traceBeginAndSlog("StartSensorNotification");
- mSystemServiceManager.startService(SensorNotificationService.class);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
+ || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
+ traceBeginAndSlog("StartAppWidgerService");
+ mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
traceEnd();
+ }
+
+ // We need to always start this service, regardless of whether the
+ // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
+ // of initializing various settings. It will internally modify its behavior
+ // based on that feature.
+ traceBeginAndSlog("StartVoiceRecognitionManager");
+ mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
+ traceEnd();
- traceBeginAndSlog("StartContextHubSystemService");
- mSystemServiceManager.startService(ContextHubSystemService.class);
+ if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
+ traceBeginAndSlog("StartGestureLauncher");
+ mSystemServiceManager.startService(GestureLauncherService.class);
traceEnd();
}
+ traceBeginAndSlog("StartSensorNotification");
+ mSystemServiceManager.startService(SensorNotificationService.class);
+ traceEnd();
+
+ traceBeginAndSlog("StartContextHubSystemService");
+ mSystemServiceManager.startService(ContextHubSystemService.class);
+ traceEnd();
traceBeginAndSlog("StartDiskStatsService");
try {
@@ -1375,16 +1348,14 @@ public final class SystemServer {
traceEnd();
}
- if (!disableNetwork && !disableNetworkTime) {
- traceBeginAndSlog("StartNetworkTimeUpdateService");
- try {
- networkTimeUpdater = new NetworkTimeUpdateService(context);
- ServiceManager.addService("network_time_update_service", networkTimeUpdater);
- } catch (Throwable e) {
- reportWtf("starting NetworkTimeUpdate service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartNetworkTimeUpdateService");
+ try {
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ ServiceManager.addService("network_time_update_service", networkTimeUpdater);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkTimeUpdate service", e);
}
+ traceEnd();
traceBeginAndSlog("StartCommonTimeManagementService");
try {
@@ -1395,38 +1366,32 @@ public final class SystemServer {
}
traceEnd();
- if (!disableNetwork) {
- traceBeginAndSlog("CertBlacklister");
- try {
- CertBlacklister blacklister = new CertBlacklister(context);
- } catch (Throwable e) {
- reportWtf("starting CertBlacklister", e);
- }
- traceEnd();
+ traceBeginAndSlog("CertBlacklister");
+ try {
+ CertBlacklister blacklister = new CertBlacklister(context);
+ } catch (Throwable e) {
+ reportWtf("starting CertBlacklister", e);
}
+ traceEnd();
- if (!disableNetwork && !disableNonCoreServices && EmergencyAffordanceManager.ENABLED) {
+ if (EmergencyAffordanceManager.ENABLED) {
// EmergencyMode service
traceBeginAndSlog("StartEmergencyAffordanceService");
mSystemServiceManager.startService(EmergencyAffordanceService.class);
traceEnd();
}
- if (!disableNonCoreServices) {
- // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
- traceBeginAndSlog("StartDreamManager");
- mSystemServiceManager.startService(DreamManagerService.class);
- traceEnd();
- }
+ // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
+ traceBeginAndSlog("StartDreamManager");
+ mSystemServiceManager.startService(DreamManagerService.class);
+ traceEnd();
- if (!disableNonCoreServices) {
- traceBeginAndSlog("AddGraphicsStatsService");
- ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE,
- new GraphicsStatsService(context));
- traceEnd();
- }
+ traceBeginAndSlog("AddGraphicsStatsService");
+ ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE,
+ new GraphicsStatsService(context));
+ traceEnd();
- if (!disableNonCoreServices && CoverageService.ENABLED) {
+ if (CoverageService.ENABLED) {
traceBeginAndSlog("AddCoverageService");
ServiceManager.addService(CoverageService.COVERAGE_SERVICE, new CoverageService());
traceEnd();
@@ -1477,38 +1442,37 @@ public final class SystemServer {
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartMediaRouterService");
- try {
- mediaRouter = new MediaRouterService(context);
- ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter);
- } catch (Throwable e) {
- reportWtf("starting MediaRouterService", e);
- }
- traceEnd();
-
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- traceBeginAndSlog("StartFingerprintSensor");
- mSystemServiceManager.startService(FingerprintService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartMediaRouterService");
+ try {
+ mediaRouter = new MediaRouterService(context);
+ ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter);
+ } catch (Throwable e) {
+ reportWtf("starting MediaRouterService", e);
+ }
+ traceEnd();
- traceBeginAndSlog("StartBackgroundDexOptService");
- try {
- BackgroundDexOptService.schedule(context);
- } catch (Throwable e) {
- reportWtf("starting StartBackgroundDexOptService", e);
- }
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ traceBeginAndSlog("StartFingerprintSensor");
+ mSystemServiceManager.startService(FingerprintService.class);
traceEnd();
+ }
- traceBeginAndSlog("StartPruneInstantAppsJobService");
- try {
- PruneInstantAppsJobService.schedule(context);
- } catch (Throwable e) {
- reportWtf("StartPruneInstantAppsJobService", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartBackgroundDexOptService");
+ try {
+ BackgroundDexOptService.schedule(context);
+ } catch (Throwable e) {
+ reportWtf("starting StartBackgroundDexOptService", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartPruneInstantAppsJobService");
+ try {
+ PruneInstantAppsJobService.schedule(context);
+ } catch (Throwable e) {
+ reportWtf("StartPruneInstantAppsJobService", e);
}
+ traceEnd();
+
// LauncherAppsService uses ShortcutService.
traceBeginAndSlog("StartShortcutServiceLifecycle");
mSystemServiceManager.startService(ShortcutService.Lifecycle.class);
@@ -1519,7 +1483,7 @@ public final class SystemServer {
traceEnd();
}
- if (!disableNonCoreServices && !disableMediaProjection) {
+ if (!disableMediaProjection) {
traceBeginAndSlog("StartMediaProjectionManager");
mSystemServiceManager.startService(MediaProjectionManagerService.class);
traceEnd();
@@ -1530,17 +1494,15 @@ public final class SystemServer {
mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
traceEnd();
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartWearTimeService");
- mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS);
- mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
- traceEnd();
+ traceBeginAndSlog("StartWearTimeService");
+ mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS);
+ mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
+ traceEnd();
- if (enableLeftyService) {
- traceBeginAndSlog("StartWearLeftyService");
- mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS);
- traceEnd();
- }
+ if (enableLeftyService) {
+ traceBeginAndSlog("StartWearLeftyService");
+ mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS);
+ traceEnd();
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9f5f856d66c1..70e8a162b1cf 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -20,10 +20,12 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -72,6 +74,7 @@ import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArrayMap;
@@ -113,7 +116,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
private IPackageManager mPackageManager;
@Mock
private PackageManager mPackageManagerClient;
- private Context mContext = getContext();
+ private TestableContext mContext = spy(getContext());
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@Mock
@@ -174,15 +177,18 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
mTestableLooper = TestableLooper.get(this);
mHandler = mService.new WorkerHandler(mTestableLooper.getLooper());
// MockPackageManager - default returns ApplicationInfo with matching calling UID
+ mContext.setMockPackageManager(mPackageManagerClient);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = mUid;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
.thenReturn(applicationInfo);
when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenReturn(applicationInfo);
+ when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
final LightsManager mockLightsManager = mock(LightsManager.class);
when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
// write to a test file; the system file isn't readable from tests
mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -227,6 +233,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
+ assertNotNull(mBinderService.getNotificationChannel(PKG, TEST_CHANNEL_ID));
}
@After
@@ -1338,6 +1345,72 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
}
@Test
+ public void testSetListenerAccessForUser() throws Exception {
+ UserHandle user = UserHandle.of(10);
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ try {
+ mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
+ verify(mListeners, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), true, true);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), false, true);
+ verify(mAssistants, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
+ public void testSetAssistantAccessForUser() throws Exception {
+ UserHandle user = UserHandle.of(10);
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ try {
+ mBinderService.setNotificationAssistantAccessGrantedForUser(
+ c, user.getIdentifier(), true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), true, true);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), false, true);
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
+ public void testSetDndAccessForUser() throws Exception {
+ UserHandle user = UserHandle.of(10);
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ try {
+ mBinderService.setNotificationPolicyAccessGrantedForUser(
+ c.getPackageName(), user.getIdentifier(), true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.getPackageName(), user.getIdentifier(), true, true);
+ verify(mAssistants, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testSetListenerAccess() throws Exception {
ComponentName c = ComponentName.unflattenFromString("package/Component");
try {
@@ -1401,9 +1474,9 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
mBinderService.setNotificationListenerAccessGranted(c, true);
verify(mListeners, never()).setPackageOrComponentEnabled(
- c.flattenToString(), 0, true, true);
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
verify(mConditionProviders, never()).setPackageOrComponentEnabled(
- c.flattenToString(), 0, false, true);
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
verify(mAssistants, never()).setPackageOrComponentEnabled(
any(), anyInt(), anyBoolean(), anyBoolean());
}
@@ -1414,11 +1487,10 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
ComponentName c = ComponentName.unflattenFromString("package/Component");
mBinderService.setNotificationAssistantAccessGranted(c, true);
-
verify(mListeners, never()).setPackageOrComponentEnabled(
- c.flattenToString(), 0, true, true);
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
verify(mConditionProviders, never()).setPackageOrComponentEnabled(
- c.flattenToString(), 0, false, true);
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
verify(mAssistants, never()).setPackageOrComponentEnabled(
any(), anyInt(), anyBoolean(), anyBoolean());
}
@@ -1430,14 +1502,77 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
verify(mListeners, never()).setPackageOrComponentEnabled(
- c.flattenToString(), 0, true, true);
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
verify(mConditionProviders, never()).setPackageOrComponentEnabled(
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mAssistants, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
+ public void testSetListenerAccess_doesNothingOnLowRam_exceptWatch() throws Exception {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ try {
+ mBinderService.setNotificationListenerAccessGranted(c, true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mListeners, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 0, true, true);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
c.flattenToString(), 0, false, true);
verify(mAssistants, never()).setPackageOrComponentEnabled(
any(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
+ public void testSetAssistantAccess_doesNothingOnLowRam_exceptWatch() throws Exception {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ try {
+ mBinderService.setNotificationAssistantAccessGranted(c, true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 0, false, true);
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 0, true, true);
+ }
+
+ @Test
+ public void testSetDndAccess_doesNothingOnLowRam_exceptWatch() throws Exception {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ try {
+ mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.getPackageName(), 0, true, true);
+ verify(mAssistants, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testOnlyAutogroupIfGroupChanged_noPriorNoti_autogroups() throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
mService.addEnqueuedNotification(r);
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
new file mode 100644
index 000000000000..bec46db2b7ab
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -0,0 +1,316 @@
+/*
+ * 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 static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.graphics.Bitmap.Config.ARGB_8888;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.view.IWindowManager;
+
+import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Note: Currently, we only support fetching the screenshot for the current application, so the
+ * screenshot checks are hardcoded accordingly.
+ *
+ * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AssistDataRequesterTest extends ActivityTestsBase {
+
+ private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
+
+ private static final boolean CURRENT_ACTIVITY_ASSIST_ALLOWED = true;
+ private static final boolean CALLER_ASSIST_STRUCTURE_ALLOWED = true;
+ private static final boolean CALLER_ASSIST_SCREENSHOT_ALLOWED = true;
+ private static final boolean FETCH_DATA = true;
+ private static final boolean FETCH_SCREENSHOTS = true;
+
+ private static final int TEST_UID = 0;
+ private static final String TEST_PACKAGE = "";
+
+ private Context mContext;
+ private AssistDataRequester mDataRequester;
+ private Callbacks mCallbacks;
+ private Object mCallbacksLock;
+ private Handler mHandler;
+ private IActivityManager mAm;
+ private IWindowManager mWm;
+ private AppOpsManager mAppOpsManager;
+
+ /**
+ * The requests to fetch assist data are done incrementally from the text thread, and we
+ * immediately post onto the main thread handler below, which would immediately make the
+ * callback and decrement the pending counts. In order to assert the pending counts, we defer
+ * the callbacks on the test-side until after we flip the gate, after which we can drain the
+ * main thread handler and make assertions on the actual callbacks
+ */
+ private CountDownLatch mGate;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mAm = mock(IActivityManager.class);
+ mWm = mock(IWindowManager.class);
+ mAppOpsManager = mock(AppOpsManager.class);
+ mContext = InstrumentationRegistry.getContext();
+ mHandler = new Handler(Looper.getMainLooper());
+ mCallbacksLock = new Object();
+ mCallbacks = new Callbacks();
+ mDataRequester = new AssistDataRequester(mContext, mAm, mWm, mAppOpsManager, mCallbacks,
+ mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
+
+ // Gate the continuation of the assist data callbacks until we are ready within the tests
+ mGate = new CountDownLatch(1);
+ doAnswer(invocation -> {
+ mHandler.post(() -> {
+ try {
+ mGate.await(10, TimeUnit.SECONDS);
+ mDataRequester.onHandleAssistData(new Bundle());
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to wait", e);
+ }
+ });
+ return true;
+ }).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(), anyBoolean(),
+ anyBoolean());
+ doAnswer(invocation -> {
+ mHandler.post(() -> {
+ try {
+ mGate.await(10, TimeUnit.SECONDS);
+ mDataRequester.onHandleAssistScreenshot(Bitmap.createBitmap(1, 1,
+ ARGB_8888));
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to wait", e);
+ }
+ });
+ return true;
+ }).when(mWm).requestAssistScreenshot(any());
+ }
+
+ private void setupMocks(boolean currentActivityAssistAllowed, boolean assistStructureAllowed,
+ boolean assistScreenshotAllowed) throws Exception {
+ doReturn(currentActivityAssistAllowed).when(mAm).isAssistDataAllowedOnCurrentActivity();
+ doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
+ .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
+ doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
+ .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
+ }
+
+ @Test
+ public void testRequestData() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(5, 5, 1, 1);
+ }
+
+ @Test
+ public void testEmptyActivities_expectNoCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 0, 0, 0);
+ }
+
+ @Test
+ public void testCurrentAppDisallow_expectNoCallbacks() throws Exception {
+ setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 0, 0, 0);
+ }
+
+ @Test
+ public void testProcessPendingData() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mCallbacks.canHandleReceivedData = false;
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertTrue(mDataRequester.getPendingDataCount() == 5);
+ assertTrue(mDataRequester.getPendingScreenshotCount() == 1);
+ mGate.countDown();
+ waitForIdle(mHandler);
+
+ // Callbacks still not ready to receive, but all pending data is received
+ assertTrue(mDataRequester.getPendingDataCount() == 0);
+ assertTrue(mDataRequester.getPendingScreenshotCount() == 0);
+ assertTrue(mCallbacks.receivedData.isEmpty());
+ assertTrue(mCallbacks.receivedScreenshots.isEmpty());
+
+ mCallbacks.canHandleReceivedData = true;
+ mDataRequester.processPendingAssistData();
+ assertTrue(mCallbacks.receivedData.size() == 5);
+ assertTrue(mCallbacks.receivedScreenshots.size() == 1);
+ }
+
+ @Test
+ public void testNoFetchData_expectNoCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 0, 0, 0);
+ }
+
+ @Test
+ public void testDisallowAssistStructure_expectNullDataCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ // Expect a single null data when the appops is denied
+ assertReceivedDataCount(0, 1, 0, 0);
+ }
+
+ @Test
+ public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+ doReturn(false).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(),
+ anyBoolean(), anyBoolean());
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ // Expect a single null data when requestAssistContextExtras() fails
+ assertReceivedDataCount(0, 1, 0, 0);
+ }
+
+ @Test
+ public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(5, 5, 0, 0);
+ }
+
+ @Test
+ public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ !CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ // Expect a single null screenshot when the appops is denied
+ assertReceivedDataCount(5, 5, 0, 1);
+ }
+
+ private void assertReceivedDataCount(int numPendingData, int numReceivedData,
+ int numPendingScreenshots, int numReceivedScreenshots) throws Exception {
+ assertTrue("Expected " + numPendingData + " pending data, got "
+ + mDataRequester.getPendingDataCount(),
+ mDataRequester.getPendingDataCount() == numPendingData);
+ assertTrue("Expected " + numPendingScreenshots + " pending screenshots, got "
+ + mDataRequester.getPendingScreenshotCount(),
+ mDataRequester.getPendingScreenshotCount() == numPendingScreenshots);
+ mGate.countDown();
+ waitForIdle(mHandler);
+ assertTrue("Expected " + numReceivedData + " data, received "
+ + mCallbacks.receivedData.size(),
+ mCallbacks.receivedData.size() == numReceivedData);
+ assertTrue("Expected " + numReceivedScreenshots + " screenshots, received "
+ + mCallbacks.receivedScreenshots.size(),
+ mCallbacks.receivedScreenshots.size() == numReceivedScreenshots);
+ }
+
+ private List<IBinder> createActivityList(int size) {
+ ArrayList<IBinder> activities = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ activities.add(mock(IBinder.class));
+ }
+ return activities;
+ }
+
+ public void waitForIdle(Handler h) throws Exception {
+ if (Looper.myLooper() == h.getLooper()) {
+ throw new RuntimeException("This method can not be called from the waiting looper");
+ }
+ CountDownLatch latch = new CountDownLatch(1);
+ h.post(() -> latch.countDown());
+ latch.await(2, TimeUnit.SECONDS);
+ }
+
+ private static class Callbacks implements AssistDataRequesterCallbacks {
+
+ boolean canHandleReceivedData = true;
+ ArrayList<Bundle> receivedData = new ArrayList<>();
+ ArrayList<Bitmap> receivedScreenshots = new ArrayList<>();
+
+ @Override
+ public boolean canHandleReceivedAssistDataLocked() {
+ return canHandleReceivedData;
+ }
+
+ @Override
+ public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+ receivedData.add(data);
+ }
+
+ @Override
+ public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+ receivedScreenshots.add(screenshot);
+ }
+ }
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 44a79ab3a6f3..1adfb67cd6ed 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -148,7 +148,7 @@ public class LockTaskControllerTest {
// THEN the lock task mode state should be LOCKED
assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
// THEN the task should be locked
- assertTrue(mLockTaskController.checkLockedTask(tr));
+ assertTrue(mLockTaskController.isTaskLocked(tr));
// THEN lock task mode should be started
verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
@@ -167,8 +167,8 @@ public class LockTaskControllerTest {
// THEN the lock task mode state should be LOCKED
assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
// THEN neither of the tasks should be able to move to back of stack
- assertTrue(mLockTaskController.checkLockedTask(tr1));
- assertTrue(mLockTaskController.checkLockedTask(tr2));
+ assertTrue(mLockTaskController.isTaskLocked(tr1));
+ assertTrue(mLockTaskController.isTaskLocked(tr2));
// THEN lock task mode should be started
verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
@@ -197,7 +197,7 @@ public class LockTaskControllerTest {
// THEN the lock task mode state should be PINNED
assertEquals(LOCK_TASK_MODE_PINNED, mLockTaskController.getLockTaskModeState());
// THEN the task should be locked
- assertTrue(mLockTaskController.checkLockedTask(tr));
+ assertTrue(mLockTaskController.isTaskLocked(tr));
// THEN lock task mode should be started
verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
@@ -238,36 +238,36 @@ public class LockTaskControllerTest {
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN the same caller calls stopLockTaskMode
- mLockTaskController.stopLockTaskMode(false, TEST_UID);
+ mLockTaskController.stopLockTaskMode(tr, false, TEST_UID);
// THEN the lock task mode should be NONE
assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
// THEN the task should no longer be locked
- assertFalse(mLockTaskController.checkLockedTask(tr));
+ assertFalse(mLockTaskController.isTaskLocked(tr));
// THEN lock task mode should have been finished
verifyLockTaskStopped(times(1));
}
@Test(expected = SecurityException.class)
- public void testStopLockTaskMode_DifferentCaller() throws Exception {
+ public void testStopLockTaskMode_differentCaller() throws Exception {
// GIVEN one task record with whitelisted auth that is in lock task mode
TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN a different caller calls stopLockTaskMode
- mLockTaskController.stopLockTaskMode(false, TEST_UID + 1);
+ mLockTaskController.stopLockTaskMode(tr, false, TEST_UID + 1);
// THEN security exception should be thrown, because different caller tried to unlock
}
@Test
- public void testStopLockTaskMode_SystemCaller() throws Exception {
+ public void testStopLockTaskMode_systemCaller() throws Exception {
// GIVEN one task record with whitelisted auth that is in lock task mode
TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN system calls stopLockTaskMode
- mLockTaskController.stopLockTaskMode(true, SYSTEM_UID);
+ mLockTaskController.stopLockTaskMode(tr, true, SYSTEM_UID);
// THEN lock task mode should still be active
assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
@@ -282,19 +282,40 @@ public class LockTaskControllerTest {
mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
// WHEN calling stopLockTaskMode
- mLockTaskController.stopLockTaskMode(false, TEST_UID);
+ mLockTaskController.stopLockTaskMode(tr2, false, TEST_UID);
// THEN the lock task mode should still be active
assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
// THEN the first task should still be locked
- assertTrue(mLockTaskController.checkLockedTask(tr1));
+ assertTrue(mLockTaskController.isTaskLocked(tr1));
// THEN the top task should no longer be locked
- assertFalse(mLockTaskController.checkLockedTask(tr2));
+ assertFalse(mLockTaskController.isTaskLocked(tr2));
// THEN lock task mode should not have been finished
verifyLockTaskStopped(never());
}
@Test
+ public void testStopLockTaskMode_rootTask() throws Exception {
+ // GIVEN two task records with whitelisted auth that is in lock task mode
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+ mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+ // WHEN calling stopLockTaskMode on the root task
+ mLockTaskController.stopLockTaskMode(tr1, false, TEST_UID);
+
+ // THEN the lock task mode should be inactive
+ assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+ // THEN the first task should no longer be locked
+ assertFalse(mLockTaskController.isTaskLocked(tr1));
+ // THEN the top task should no longer be locked
+ assertFalse(mLockTaskController.isTaskLocked(tr2));
+ // THEN lock task mode should be finished
+ verifyLockTaskStopped(times(1));
+ }
+
+ @Test
public void testStopLockTaskMode_pinned() throws Exception {
// GIVEN one task records that is in pinned mode
TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
@@ -307,12 +328,12 @@ public class LockTaskControllerTest {
reset(mStatusBarService);
// WHEN calling stopLockTask
- mLockTaskController.stopLockTaskMode(true, SYSTEM_UID);
+ mLockTaskController.stopLockTaskMode(null, true, SYSTEM_UID);
// THEN the lock task mode should no longer be active
assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
// THEN the task should no longer be locked
- assertFalse(mLockTaskController.checkLockedTask(tr));
+ assertFalse(mLockTaskController.isTaskLocked(tr));
// THEN lock task mode should have been finished
verifyLockTaskStopped(times(1));
// THEN the keyguard should be shown
@@ -322,6 +343,27 @@ public class LockTaskControllerTest {
}
@Test
+ public void testClearLockedTasks() throws Exception {
+ // GIVEN two task records with whitelisted auth that is in lock task mode
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+ mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+ // WHEN calling stopLockTaskMode on the root task
+ mLockTaskController.clearLockedTasks("testClearLockedTasks");
+
+ // THEN the lock task mode should be inactive
+ assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+ // THEN the first task should no longer be locked
+ assertFalse(mLockTaskController.isTaskLocked(tr1));
+ // THEN the top task should no longer be locked
+ assertFalse(mLockTaskController.isTaskLocked(tr2));
+ // THEN lock task mode should be finished
+ verifyLockTaskStopped(times(1));
+ }
+
+ @Test
public void testUpdateLockTaskPackages() throws Exception {
String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
String[] whitelist2 = {TEST_PACKAGE_NAME};
@@ -367,8 +409,8 @@ public class LockTaskControllerTest {
mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
- assertTrue(mLockTaskController.checkLockedTask(tr1));
- assertTrue(mLockTaskController.checkLockedTask(tr2));
+ assertTrue(mLockTaskController.isTaskLocked(tr1));
+ assertTrue(mLockTaskController.isTaskLocked(tr2));
verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
// WHEN removing one package from whitelist
@@ -377,10 +419,10 @@ public class LockTaskControllerTest {
// THEN the task running that package should be stopped
verify(tr2).performClearTaskLocked();
- assertFalse(mLockTaskController.checkLockedTask(tr2));
+ assertFalse(mLockTaskController.isTaskLocked(tr2));
// THEN the other task should remain locked
assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
- assertTrue(mLockTaskController.checkLockedTask(tr1));
+ assertTrue(mLockTaskController.isTaskLocked(tr1));
verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
// WHEN removing the last package from whitelist
@@ -389,7 +431,7 @@ public class LockTaskControllerTest {
// THEN the last task should be cleared, and the system should quit LockTask mode
verify(tr1).performClearTaskLocked();
- assertFalse(mLockTaskController.checkLockedTask(tr1));
+ assertFalse(mLockTaskController.isTaskLocked(tr1));
assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
verifyLockTaskStopped(times(1));
}
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 3c9b54215f80..f5ea60ff5929 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -16,7 +16,7 @@
package com.android.server.am;
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -382,8 +382,8 @@ public class RecentTasksTest extends ActivityTestsBase {
assertSecurityException(expectCallable,
() -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mService.moveTaskToDockedStack(0, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, true,
- true, new Rect()));
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect()));
assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
@@ -428,6 +428,7 @@ public class RecentTasksTest extends ActivityTestsBase {
assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
assertSecurityException(expectCallable, () -> mService.cancelTaskThumbnailTransition(0));
+ assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null, 0));
}
private void testGetTasksApis(boolean expectCallable) {
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index d6d0209bca7d..6fdb02988feb 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -60,7 +60,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -104,7 +103,7 @@ public class UserControllerTest extends AndroidTestCase {
public void setUp() throws Exception {
super.setUp();
mInjector = Mockito.spy(new TestInjector(getContext()));
- doNothing().when(mInjector).clearLockTaskMode(anyString());
+ doNothing().when(mInjector).clearAllLockedTasks(anyString());
doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
doNothing().when(mInjector).stackSupervisorResumeFocusedStackTopActivity();
@@ -126,7 +125,7 @@ public class UserControllerTest extends AndroidTestCase {
Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
- Mockito.verify(mInjector).clearLockTaskMode(anyString());
+ Mockito.verify(mInjector).clearAllLockedTasks(anyString());
startForegroundUserAssertions();
}
@@ -136,7 +135,7 @@ public class UserControllerTest extends AndroidTestCase {
Mockito.verify(
mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector, never()).clearLockTaskMode(anyString());
+ Mockito.verify(mInjector, never()).clearAllLockedTasks(anyString());
startBackgroundUserAssertions();
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 9ad7addab858..ea4f4b907824 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -193,7 +193,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
token.setFillsParent(false);
- // Can specify orientation if app doesn't fill parent. Allowed for SDK <= 25.
+ // Can specify orientation if app doesn't fill parent.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
token.setFillsParent(true);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
new file mode 100644
index 000000000000..ad9aea748ebc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.wm.proto.WindowManagerTraceProto;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Test class for {@link WindowTracing}.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.WindowTracingTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WindowTracingTest extends WindowTestsBase {
+
+ private static final byte[] MAGIC_HEADER = new byte[] {
+ 0x9, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45,
+ };
+
+ private Context mTestContext;
+ private WindowTracing mWindowTracing;
+ private WindowManagerService mWmMock;
+ private File mFile;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mWmMock = mock(WindowManagerService.class);
+
+ mTestContext = InstrumentationRegistry.getContext();
+
+ mFile = mTestContext.getFileStreamPath("tracing_test.dat");
+ mFile.delete();
+
+ mWindowTracing = new WindowTracing(mFile);
+ }
+
+ @Test
+ public void isEnabled_returnsFalseByDefault() throws Exception {
+ assertFalse(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsTrueAfterStart() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ assertTrue(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsFalseAfterStop() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ mWindowTracing.stopTrace(mock(PrintWriter.class));
+ assertFalse(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void trace_discared_whenNotTracing() throws Exception {
+ mWindowTracing.traceStateLocked("where", mWmMock);
+ verifyZeroInteractions(mWmMock);
+ }
+
+ @Test
+ public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ mWindowTracing.traceStateLocked("where", mWmMock);
+
+ verify(mWmMock).writeToProtoLocked(any(), eq(true));
+ }
+
+ @Test
+ public void traceFile_startsWithMagicHeader() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ mWindowTracing.stopTrace(mock(PrintWriter.class));
+
+ byte[] header = new byte[MAGIC_HEADER.length];
+ try (InputStream is = new FileInputStream(mFile)) {
+ assertEquals(MAGIC_HEADER.length, is.read(header));
+ assertArrayEquals(MAGIC_HEADER, header);
+ }
+ }
+
+ @Test
+ @Ignore("Figure out why this test is crashing when setting up mWmMock.")
+ public void tracing_endsUpInFile() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+
+ doAnswer((inv) -> {
+ inv.<ProtoOutputStream>getArgument(0).write(
+ WindowManagerTraceProto.WHERE, "TEST_WM_PROTO");
+ return null;
+ }).when(mWmMock).writeToProtoLocked(any(), any());
+ mWindowTracing.traceStateLocked("TEST_WHERE", mWmMock);
+
+ mWindowTracing.stopTrace(mock(PrintWriter.class));
+
+ byte[] file = new byte[1000];
+ int fileLength;
+ try (InputStream is = new FileInputStream(mFile)) {
+ fileLength = is.read(file);
+ assertTrue(containsBytes(file, fileLength,
+ "TEST_WHERE".getBytes(StandardCharsets.UTF_8)));
+ assertTrue(containsBytes(file, fileLength,
+ "TEST_WM_PROTO".getBytes(StandardCharsets.UTF_8)));
+ }
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ mFile.delete();
+ }
+
+ /** Return true if {@code needle} appears anywhere in {@code haystack[0..length]} */
+ boolean containsBytes(byte[] haystack, int haystackLenght, byte[] needle) {
+ Preconditions.checkArgument(haystackLenght > 0);
+ Preconditions.checkArgument(needle.length > 0);
+
+ outer: for (int i = 0; i <= haystackLenght - needle.length; i++) {
+ for (int j = 0; j < needle.length; j++) {
+ if (haystack[i+j] != needle[j]) {
+ continue outer;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Test
+ public void test_containsBytes() {
+ byte[] haystack = "hello_world".getBytes(StandardCharsets.UTF_8);
+ assertTrue(containsBytes(haystack, haystack.length,
+ "hello".getBytes(StandardCharsets.UTF_8)));
+ assertTrue(containsBytes(haystack, haystack.length,
+ "world".getBytes(StandardCharsets.UTF_8)));
+ assertFalse(containsBytes(haystack, 6,
+ "world".getBytes(StandardCharsets.UTF_8)));
+ assertFalse(containsBytes(haystack, haystack.length,
+ "world_".getBytes(StandardCharsets.UTF_8)));
+ assertFalse(containsBytes(haystack, haystack.length,
+ "absent".getBytes(StandardCharsets.UTF_8)));
+ }
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b040a6324dbb..7541b92dbe2c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -55,6 +55,7 @@ import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
@@ -162,13 +163,16 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
mInfo.getServiceInfo().applicationInfo.uid, mHandler);
}
List<IBinder> activityTokens = null;
- if (activityToken == null) {
+ if (activityToken != null) {
+ activityTokens = new ArrayList<>();
+ activityTokens.add(activityToken);
+ } else {
// Let's get top activities from all visible stacks
activityTokens = LocalServices.getService(ActivityManagerInternal.class)
.getTopVisibleActivities();
}
return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
- activityToken, activityTokens);
+ activityTokens);
}
public boolean hideSessionLocked() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index d394d6311ba5..925219d64967 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -16,6 +16,16 @@
package com.android.server.voiceinteraction;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
@@ -43,31 +53,24 @@ import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
import android.util.Slog;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.app.AssistUtils;
-import com.android.internal.app.IAssistScreenshotReceiver;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
+import com.android.server.am.AssistDataRequester;
+import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-
-final class VoiceInteractionSessionConnection implements ServiceConnection {
+final class VoiceInteractionSessionConnection implements ServiceConnection,
+ AssistDataRequesterCallbacks {
final static String TAG = "VoiceInteractionServiceManager";
- private static final String KEY_RECEIVER_EXTRA_COUNT = "count";
- private static final String KEY_RECEIVER_EXTRA_INDEX = "index";
-
final IBinder mToken = new Binder();
final Object mLock;
final ComponentName mSessionComponentName;
@@ -90,27 +93,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
IVoiceInteractionSessionService mService;
IVoiceInteractionSession mSession;
IVoiceInteractor mInteractor;
- boolean mHaveAssistData;
- int mPendingAssistDataCount;
- ArrayList<AssistDataForActivity> mAssistData = new ArrayList<>();
- boolean mHaveScreenshot;
- Bitmap mScreenshot;
ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
-
- static class AssistDataForActivity {
- int activityIndex;
- int activityCount;
- Bundle data;
-
- public AssistDataForActivity(Bundle data) {
- this.data = data;
- Bundle receiverExtras = data.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS);
- if (receiverExtras != null) {
- activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX);
- activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT);
- }
- }
- }
+ AssistDataRequester mAssistDataRequester;
IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -146,32 +130,6 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
}
};
- final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) throws RemoteException {
- synchronized (mLock) {
- if (mShown) {
- mHaveAssistData = true;
- mAssistData.add(new AssistDataForActivity(resultData));
- deliverSessionDataLocked();
- }
- }
- }
- };
-
- final IAssistScreenshotReceiver mScreenshotReceiver = new IAssistScreenshotReceiver.Stub() {
- @Override
- public void send(Bitmap screenshot) throws RemoteException {
- synchronized (mLock) {
- if (mShown) {
- mHaveScreenshot = true;
- mScreenshot = screenshot;
- deliverSessionDataLocked();
- }
- }
- }
- };
-
public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
Context context, Callback callback, int callingUid, Handler handler) {
mLock = lock;
@@ -185,6 +143,9 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mAppOps = context.getSystemService(AppOpsManager.class);
+ mAssistDataRequester = new AssistDataRequester(mContext, mAm, mIWindowManager,
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
+ this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
IBinder permOwner = null;
try {
permOwner = mAm.newUriPermissionOwner("voicesession:"
@@ -224,8 +185,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
}
public boolean showLocked(Bundle args, int flags, int disabledContext,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken,
- List<IBinder> topActivities) {
+ IVoiceInteractionSessionShowCallback showCallback, List<IBinder> topActivities) {
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -233,75 +193,18 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
| Context.BIND_FOREGROUND_SERVICE,
new UserHandle(mUser));
}
+
mShown = true;
- boolean isAssistDataAllowed = true;
- try {
- isAssistDataAllowed = mAm.isAssistDataAllowedOnCurrentActivity();
- } catch (RemoteException e) {
- }
- disabledContext |= getUserDisabledShowContextLocked();
- boolean structureEnabled = isAssistDataAllowed
- && (disabledContext&VoiceInteractionSession.SHOW_WITH_ASSIST) == 0;
- boolean screenshotEnabled = isAssistDataAllowed && structureEnabled
- && (disabledContext&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0;
mShowArgs = args;
mShowFlags = flags;
- mHaveAssistData = false;
- mPendingAssistDataCount = 0;
- boolean needDisclosure = false;
- if ((flags&VoiceInteractionSession.SHOW_WITH_ASSIST) != 0) {
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
- mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
- && structureEnabled) {
- mAssistData.clear();
- final int count = activityToken != null ? 1 : topActivities.size();
- for (int i = 0; i < count; i++) {
- IBinder topActivity = count == 1 ? activityToken : topActivities.get(i);
- try {
- MetricsLogger.count(mContext, "assist_with_context", 1);
- Bundle receiverExtras = new Bundle();
- receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
- receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, count);
- if (mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
- mAssistReceiver, receiverExtras, topActivity,
- /* focused= */ i == 0, /* newSessionId= */ i == 0)) {
- needDisclosure = true;
- mPendingAssistDataCount++;
- } else if (i == 0) {
- // Wasn't allowed... given that, let's not do the screenshot either.
- mHaveAssistData = true;
- mAssistData.clear();
- screenshotEnabled = false;
- break;
- }
- } catch (RemoteException e) {
- }
- }
- } else {
- mHaveAssistData = true;
- mAssistData.clear();
- }
- } else {
- mAssistData.clear();
- }
- mHaveScreenshot = false;
- if ((flags&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0) {
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_SCREENSHOT, mCallingUid,
- mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
- && screenshotEnabled) {
- try {
- MetricsLogger.count(mContext, "assist_with_screen", 1);
- needDisclosure = true;
- mIWindowManager.requestAssistScreenshot(mScreenshotReceiver);
- } catch (RemoteException e) {
- }
- } else {
- mHaveScreenshot = true;
- mScreenshot = null;
- }
- } else {
- mScreenshot = null;
- }
+
+ mAssistDataRequester.requestAssistData(topActivities,
+ (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
+ (disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
+ mCallingUid, mSessionComponentName.getPackageName());
+
+ boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
+ || mAssistDataRequester.getPendingScreenshotCount() > 0;
if (needDisclosure && AssistUtils.shouldDisclose(mContext, mSessionComponentName)) {
mHandler.post(mShowAssistDisclosureRunnable);
}
@@ -312,7 +215,6 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
mShowFlags = 0;
} catch (RemoteException e) {
}
- deliverSessionDataLocked();
} else if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
}
@@ -328,6 +230,57 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
return false;
}
+ @Override
+ public boolean canHandleReceivedAssistDataLocked() {
+ return mSession != null;
+ }
+
+ @Override
+ public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+ if (data == null) {
+ try {
+ mSession.handleAssist(null, null, null, 0, 0);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ } else {
+ final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
+ final AssistStructure structure = data.getParcelable(ASSIST_KEY_STRUCTURE);
+ final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
+ final int uid = data.getInt(Intent.EXTRA_ASSIST_UID, -1);
+ if (uid >= 0 && content != null) {
+ Intent intent = content.getIntent();
+ if (intent != null) {
+ ClipData clipData = intent.getClipData();
+ if (clipData != null && Intent.isAccessUriMode(intent.getFlags())) {
+ grantClipDataPermissions(clipData, intent.getFlags(), uid,
+ mCallingUid, mSessionComponentName.getPackageName());
+ }
+ }
+ ClipData clipData = content.getClipData();
+ if (clipData != null) {
+ grantClipDataPermissions(clipData, FLAG_GRANT_READ_URI_PERMISSION,
+ uid, mCallingUid, mSessionComponentName.getPackageName());
+ }
+ }
+ try {
+ mSession.handleAssist(assistData, structure, content, activityIndex,
+ activityCount);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ }
+ }
+
+ @Override
+ public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+ try {
+ mSession.handleScreenshot(screenshot);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ }
+
void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) {
if (!"content".equals(uri.getScheme())) {
return;
@@ -341,7 +294,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
uri = ContentProvider.getUriWithoutUserId(uri);
mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg,
- uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
+ uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
} catch (RemoteException e) {
} catch (SecurityException e) {
Slog.w(TAG, "Can't propagate permission", e);
@@ -370,89 +323,13 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
}
}
- void deliverSessionDataLocked() {
- if (mSession == null) {
- return;
- }
- if (mHaveAssistData) {
- AssistDataForActivity assistData;
- if (mAssistData.isEmpty()) {
- // We're not actually going to get any data, deliver some nothing
- try {
- mSession.handleAssist(null, null, null, 0, 0);
- } catch (RemoteException e) {
- }
- } else {
- while (!mAssistData.isEmpty()) {
- if (mPendingAssistDataCount <= 0) {
- Slog.e(TAG, "mPendingAssistDataCount is " + mPendingAssistDataCount);
- }
- mPendingAssistDataCount--;
- assistData = mAssistData.remove(0);
- if (assistData.data == null) {
- try {
- mSession.handleAssist(null, null, null, assistData.activityIndex,
- assistData.activityCount);
- } catch (RemoteException e) {
- }
- } else {
- deliverSessionDataLocked(assistData);
- }
- }
- }
- if (mPendingAssistDataCount <= 0) {
- mHaveAssistData = false;
- } // else, more to come
- }
- if (mHaveScreenshot) {
- try {
- mSession.handleScreenshot(mScreenshot);
- } catch (RemoteException e) {
- }
- mScreenshot = null;
- mHaveScreenshot = false;
- }
- }
-
- private void deliverSessionDataLocked(AssistDataForActivity assistDataForActivity) {
- Bundle assistData = assistDataForActivity.data.getBundle(
- VoiceInteractionSession.KEY_DATA);
- AssistStructure structure = assistDataForActivity.data.getParcelable(
- VoiceInteractionSession.KEY_STRUCTURE);
- AssistContent content = assistDataForActivity.data.getParcelable(
- VoiceInteractionSession.KEY_CONTENT);
- int uid = assistDataForActivity.data.getInt(Intent.EXTRA_ASSIST_UID, -1);
- if (uid >= 0 && content != null) {
- Intent intent = content.getIntent();
- if (intent != null) {
- ClipData data = intent.getClipData();
- if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
- grantClipDataPermissions(data, intent.getFlags(), uid,
- mCallingUid, mSessionComponentName.getPackageName());
- }
- }
- ClipData data = content.getClipData();
- if (data != null) {
- grantClipDataPermissions(data,
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- uid, mCallingUid, mSessionComponentName.getPackageName());
- }
- }
- try {
- mSession.handleAssist(assistData, structure, content,
- assistDataForActivity.activityIndex, assistDataForActivity.activityCount);
- } catch (RemoteException e) {
- }
- }
-
public boolean hideLocked() {
if (mBound) {
if (mShown) {
mShown = false;
mShowArgs = null;
mShowFlags = 0;
- mHaveAssistData = false;
- mAssistData.clear();
+ mAssistDataRequester.cancel();
mPendingShowCallbacks.clear();
if (mSession != null) {
try {
@@ -462,8 +339,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
}
try {
mAm.revokeUriPermissionFromOwner(mPermissionOwner, null,
- Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION,
mUser);
} catch (RemoteException e) {
}
@@ -529,7 +405,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
mShowFlags = 0;
} catch (RemoteException e) {
}
- deliverSessionDataLocked();
+ mAssistDataRequester.processPendingAssistData();
}
return true;
}
@@ -587,10 +463,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
pw.print(prefix); pw.print("mSession="); pw.println(mSession);
pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
}
- pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData);
- if (mHaveAssistData) {
- pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
- }
+ mAssistDataRequester.dump(prefix, pw);
}
private Runnable mShowAssistDisclosureRunnable = new Runnable() {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6fc7d23a6538..3fc220857676 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -352,6 +352,17 @@ public class CarrierConfigManager {
public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
/**
+ * Flag that specifies to use the user's own phone number as the voicemail number when there is
+ * no pre-loaded voicemail number on the SIM card.
+ * <p>
+ * {@link #KEY_DEFAULT_VM_NUMBER_STRING} takes precedence over this flag.
+ * <p>
+ * If false, the system default (*86) will be used instead.
+ */
+ public static final String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL =
+ "config_telephony_use_own_number_for_voicemail_bool";
+
+ /**
* When {@code true}, changes to the mobile data enabled switch will not cause the VT
* registration state to change. That is, turning on or off mobile data will not cause VT to be
* enabled or disabled.
@@ -1187,24 +1198,21 @@ public class CarrierConfigManager {
*/
public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
-
/**
- * @hide
- * The default value for preferred CDMA roaming mode (aka CDMA system select.)
- * CDMA_ROAMING_MODE_RADIO_DEFAULT = the default roaming mode from the radio
- * CDMA_ROAMING_MODE_HOME = Home Networks
- * CDMA_ROAMING_MODE_AFFILIATED = Roaming on Affiliated networks
- * CDMA_ROAMING_MODE_ANY = Roaming on any networks
+ * The CDMA roaming mode (aka CDMA system select).
+ *
+ * <p>The value should be one of the CDMA_ROAMING_MODE_ constants in {@link TelephonyManager}.
+ * Values other than {@link TelephonyManager#CDMA_ROAMING_MODE_RADIO_DEFAULT} (which is the
+ * default) will take precedence over user selection.
+ *
+ * @see TelephonyManager#CDMA_ROAMING_MODE_RADIO_DEFAULT
+ * @see TelephonyManager#CDMA_ROAMING_MODE_HOME
+ * @see TelephonyManager#CDMA_ROAMING_MODE_AFFILIATED
+ * @see TelephonyManager#CDMA_ROAMING_MODE_ANY
*/
public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
- /** @hide */
- public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1;
- /** @hide */
- public static final int CDMA_ROAMING_MODE_HOME = 0;
- /** @hide */
- public static final int CDMA_ROAMING_MODE_AFFILIATED = 1;
- /** @hide */
- public static final int CDMA_ROAMING_MODE_ANY = 2;
+
+
/**
* Boolean indicating if support is provided for directly dialing FDN number from FDN list.
* If false, this feature is not supported.
@@ -1535,6 +1543,13 @@ public class CarrierConfigManager {
"boosted_lte_earfcns_string_array";
/**
+ * Determine whether to use only RSRP for the number of LTE signal bars.
+ * @hide
+ */
+ public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
+ "use_only_rsrp_for_lte_signal_bar_bool";
+
+ /**
* Key identifying if voice call barring notification is required to be shown to the user.
* @hide
*/
@@ -1645,6 +1660,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
+ sDefaults.putBoolean(KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
@@ -1821,7 +1837,8 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false);
- sDefaults.putInt(KEY_CDMA_ROAMING_MODE_INT, CDMA_ROAMING_MODE_RADIO_DEFAULT);
+ sDefaults.putInt(
+ KEY_CDMA_ROAMING_MODE_INT, TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
sDefaults.putString(KEY_RCS_CONFIG_SERVER_URL_STRING, "");
// Carrier Signalling Receivers
@@ -1892,6 +1909,7 @@ public class CarrierConfigManager {
null);
sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0);
sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 9a9877a88517..f392570ecb29 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -74,6 +75,14 @@ public class MbmsDownloadSession implements AutoCloseable {
"android.telephony.action.EmbmsDownload";
/**
+ * Metadata key that specifies the component name of the service to bind to for file-download.
+ * @hide
+ */
+ @TestApi
+ public static final String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA =
+ "mbms-download-service-override";
+
+ /**
* Integer extra that Android will attach to the intent supplied via
* {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
* Indicates the result code of the download. One of
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index a8c46079148c..fb2ff7b178b1 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
@@ -62,6 +63,14 @@ public class MbmsStreamingSession implements AutoCloseable {
public static final String MBMS_STREAMING_SERVICE_ACTION =
"android.telephony.action.EmbmsStreaming";
+ /**
+ * Metadata key that specifies the component name of the service to bind to for file-download.
+ * @hide
+ */
+ @TestApi
+ public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA =
+ "mbms-streaming-service-override";
+
private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index c8b4776522c6..de02de7ba17d 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -68,6 +68,7 @@ public class SignalStrength implements Parcelable {
private int mTdScdmaRscp;
private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+ private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
/**
* Create a new SignalStrength from a intent notifier Bundle
@@ -108,6 +109,7 @@ public class SignalStrength implements Parcelable {
mLteRsrpBoost = 0;
mTdScdmaRscp = INVALID;
isGsm = true;
+ mUseOnlyRsrpForLteLevel = false;
}
/**
@@ -134,6 +136,7 @@ public class SignalStrength implements Parcelable {
mLteRsrpBoost = 0;
mTdScdmaRscp = INVALID;
isGsm = gsmFlag;
+ mUseOnlyRsrpForLteLevel = false;
}
/**
@@ -145,10 +148,10 @@ public class SignalStrength implements Parcelable {
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag) {
+ int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag, lteLevelBaseOnRsrp);
mTdScdmaRscp = tdScdmaRscp;
}
@@ -164,7 +167,7 @@ public class SignalStrength implements Parcelable {
int tdScdmaRscp, boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
mTdScdmaRscp = tdScdmaRscp;
}
@@ -180,7 +183,7 @@ public class SignalStrength implements Parcelable {
boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
}
/**
@@ -194,7 +197,7 @@ public class SignalStrength implements Parcelable {
boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsmFlag);
+ INVALID, INVALID, INVALID, 0, gsmFlag, false);
}
/**
@@ -228,7 +231,7 @@ public class SignalStrength implements Parcelable {
boolean gsm) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsm);
+ INVALID, INVALID, INVALID, 0, gsm, false);
}
/**
@@ -248,6 +251,7 @@ public class SignalStrength implements Parcelable {
* @param lteCqi
* @param lteRsrpBoost
* @param gsm
+ * @param useOnlyRsrpForLteLevel
*
* @hide
*/
@@ -255,7 +259,7 @@ public class SignalStrength implements Parcelable {
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, boolean gsm) {
+ int lteRsrpBoost, boolean gsm, boolean useOnlyRsrpForLteLevel) {
mGsmSignalStrength = gsmSignalStrength;
mGsmBitErrorRate = gsmBitErrorRate;
mCdmaDbm = cdmaDbm;
@@ -271,6 +275,7 @@ public class SignalStrength implements Parcelable {
mLteRsrpBoost = lteRsrpBoost;
mTdScdmaRscp = INVALID;
isGsm = gsm;
+ mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
if (DBG) log("initialize: " + toString());
}
@@ -293,6 +298,7 @@ public class SignalStrength implements Parcelable {
mLteRsrpBoost = s.mLteRsrpBoost;
mTdScdmaRscp = s.mTdScdmaRscp;
isGsm = s.isGsm;
+ mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
}
/**
@@ -318,6 +324,7 @@ public class SignalStrength implements Parcelable {
mLteRsrpBoost = in.readInt();
mTdScdmaRscp = in.readInt();
isGsm = (in.readInt() != 0);
+ mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
}
/**
@@ -366,6 +373,7 @@ public class SignalStrength implements Parcelable {
out.writeInt(mLteRsrpBoost);
out.writeInt(mTdScdmaRscp);
out.writeInt(isGsm ? 1 : 0);
+ out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
}
/**
@@ -449,6 +457,17 @@ public class SignalStrength implements Parcelable {
}
/**
+ * @param useOnlyRsrpForLteLevel true if it uses only RSRP for the number of LTE signal bar,
+ * otherwise false.
+ *
+ * Used by phone to use only RSRP or not for the number of LTE signal bar.
+ * @hide
+ */
+ public void setUseOnlyRsrpForLteLevel(boolean useOnlyRsrpForLteLevel) {
+ mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
+ }
+
+ /**
* @param lteRsrpBoost - signal strength offset
*
* Used by phone to set the lte signal strength offset which will be
@@ -835,6 +854,13 @@ public class SignalStrength implements Parcelable {
}
}
+ if (useOnlyRsrpForLteLevel()) {
+ log("getLTELevel - rsrp = " + rsrpIconLevel);
+ if (rsrpIconLevel != -1) {
+ return rsrpIconLevel;
+ }
+ }
+
/*
* Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
* dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
@@ -915,6 +941,15 @@ public class SignalStrength implements Parcelable {
}
/**
+ * @return true if it uses only RSRP for the number of LTE signal bar, otherwise false.
+ *
+ * @hide
+ */
+ public boolean useOnlyRsrpForLteLevel() {
+ return this.mUseOnlyRsrpForLteLevel;
+ }
+
+ /**
* @return get TD_SCDMA dbm
*
* @hide
@@ -974,7 +1009,8 @@ public class SignalStrength implements Parcelable {
+ (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
+ (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
- + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
+ + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
+ + (mUseOnlyRsrpForLteLevel ? 1 : 0));
}
/**
@@ -1008,7 +1044,8 @@ public class SignalStrength implements Parcelable {
&& mLteCqi == s.mLteCqi
&& mLteRsrpBoost == s.mLteRsrpBoost
&& mTdScdmaRscp == s.mTdScdmaRscp
- && isGsm == s.isGsm);
+ && isGsm == s.isGsm
+ && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel);
}
/**
@@ -1031,7 +1068,9 @@ public class SignalStrength implements Parcelable {
+ " " + mLteCqi
+ " " + mLteRsrpBoost
+ " " + mTdScdmaRscp
- + " " + (isGsm ? "gsm|lte" : "cdma"));
+ + " " + (isGsm ? "gsm|lte" : "cdma")
+ + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
+ "use_rsrp_and_rssnr_for_lte_level"));
}
/** Returns the signal strength related to GSM. */
@@ -1086,6 +1125,7 @@ public class SignalStrength implements Parcelable {
mLteRsrpBoost = m.getInt("lteRsrpBoost");
mTdScdmaRscp = m.getInt("TdScdma");
isGsm = m.getBoolean("isGsm");
+ mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
}
/**
@@ -1110,6 +1150,7 @@ public class SignalStrength implements Parcelable {
m.putInt("lteRsrpBoost", mLteRsrpBoost);
m.putInt("TdScdma", mTdScdmaRscp);
m.putBoolean("isGsm", isGsm);
+ m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
}
/**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 98195ada16f0..924f0deff333 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -387,6 +387,112 @@ public final class SmsManager {
}
/**
+ * Send a text based SMS with messaging options.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ * {@hide}
+ */
+ public void sendTextMessage(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ true /* persistMessage*/, priority, expectMore, validityPeriod);
+ }
+
+ private void sendTextMessageInternal(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
+ int priority, boolean expectMore, int validityPeriod) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ if (priority < 0x00 || priority > 0x03) {
+ throw new IllegalArgumentException("Invalid priority");
+ }
+
+ if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ throw new IllegalArgumentException("Invalid validity period");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ if (iccISms != null) {
+ iccISms.sendTextForSubscriberWithOptions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress, scAddress, text,
+ sentIntent, deliveryIntent, persistMessage, priority, expectMore,
+ validityPeriod);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Send a text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+ * privileges.
+ * </p>
+ *
+ * @see #sendTextMessage(String, String, String, PendingIntent,
+ * PendingIntent, int, boolean, int)
+ * @hide
+ */
+ public void sendTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,
+ boolean expectMore, int validityPeriod) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ false /* persistMessage */, priority, expectMore, validityPeriod);
+ }
+
+ /**
+ *
* Inject an SMS PDU into the android application framework.
*
* <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
@@ -544,6 +650,140 @@ public final class SmsManager {
}
/**
+ * Send a multi-part text based SMS with messaging options. The callee should have already
+ * divided the message into correctly sized parts by calling
+ * <code>divideMessage</code>.
+ *
+ * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+ * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+ *
+ * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+ * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+ * writes messages sent using this method to the SMS Provider (the default SMS app is always
+ * responsible for writing its sent messages to the SMS Provider). For information about
+ * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ * {@hide}
+ */
+ public void sendMultipartTextMessage(
+ String destinationAddress, String scAddress, ArrayList<String> parts,
+ ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents, true /* persistMessage*/);
+ }
+
+ private void sendMultipartTextMessageInternal(
+ String destinationAddress, String scAddress, List<String> parts,
+ List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+ boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+ if (parts == null || parts.size() < 1) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ if (priority < 0x00 || priority > 0x03) {
+ throw new IllegalArgumentException("Invalid priority");
+ }
+
+ if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ throw new IllegalArgumentException("Invalid validity period");
+ }
+
+ if (parts.size() > 1) {
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ if (iccISms != null) {
+ iccISms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress, scAddress,
+ parts, sentIntents, deliveryIntents, persistMessage, priority,
+ expectMore, validityPeriod);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ } else {
+ PendingIntent sentIntent = null;
+ PendingIntent deliveryIntent = null;
+ if (sentIntents != null && sentIntents.size() > 0) {
+ sentIntent = sentIntents.get(0);
+ }
+ if (deliveryIntents != null && deliveryIntents.size() > 0) {
+ deliveryIntent = deliveryIntents.get(0);
+ }
+ sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
+ sentIntent, deliveryIntent, persistMessage, priority, expectMore,
+ validityPeriod);
+ }
+ }
+
+ /**
+ * Send a multi-part text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+ * privileges.
+ * </p>
+ *
+ * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList,
+ * ArrayList, int, boolean, int)
+ * @hide
+ **/
+ public void sendMultipartTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, List<String> parts,
+ List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents, false /* persistMessage*/, priority, expectMore,
+ validityPeriod);
+ }
+
+ /**
* Send a data based SMS to a specific application port.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -1006,7 +1246,7 @@ public final class SmsManager {
* <code>getAllMessagesFromIcc</code>
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
*/
- private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+ private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
if (records != null) {
int count = records.size();
@@ -1014,7 +1254,8 @@ public final class SmsManager {
SmsRawData data = records.get(i);
// List contains all records, including "free" records (null)
if (data != null) {
- SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
+ SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
+ getSubscriptionId());
if (sms != null) {
messages.add(sms);
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index df41233559a1..a5d67c60e40b 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -271,6 +271,31 @@ public class SmsMessage {
}
/**
+ * Create an SmsMessage from an SMS EF record.
+ *
+ * @param index Index of SMS record. This should be index in ArrayList
+ * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param data Record data.
+ * @param subId Subscription Id of the SMS
+ * @return An SmsMessage representing the record.
+ *
+ * @hide
+ */
+ public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) {
+ SmsMessageBase wrappedMessage;
+
+ if (isCdmaVoice(subId)) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+ index, data);
+ } else {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+ index, data);
+ }
+
+ return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+ }
+
+ /**
* Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
* length in bytes (not hex chars) less the SMSC header
*
@@ -822,6 +847,7 @@ public class SmsMessage {
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
return (PHONE_TYPE_CDMA == activePhone);
}
+
/**
* Decide if the carrier supports long SMS.
* {@hide}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c0564c55a7e0..42c3de563492 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -953,6 +953,27 @@ public class TelephonyManager {
*/
public static final int USSD_ERROR_SERVICE_UNAVAIL = -2;
+ /**
+ * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which leaves the roaming
+ * mode set to the radio default or to the user's preference if they've indicated one.
+ */
+ public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1;
+ /**
+ * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which only permits
+ * connections on home networks.
+ */
+ public static final int CDMA_ROAMING_MODE_HOME = 0;
+ /**
+ * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
+ * affiliated networks.
+ */
+ public static final int CDMA_ROAMING_MODE_AFFILIATED = 1;
+ /**
+ * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
+ * any network.
+ */
+ public static final int CDMA_ROAMING_MODE_ANY = 2;
+
//
//
// Device Info
@@ -2145,13 +2166,16 @@ public class TelephonyManager {
* @hide
*/
public String getSimOperatorNumeric() {
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ int subId = mSubId;
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ subId = SubscriptionManager.getDefaultDataSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ subId = SubscriptionManager.getDefaultSmsSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSubscriptionId();
+ subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+ subId = SubscriptionManager.getDefaultSubscriptionId();
+ }
}
}
}
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index d8d7f48a56be..b30a3af72c3e 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,6 +36,7 @@ public final class FileServiceInfo extends ServiceInfo implements Parcelable {
/** @hide */
@SystemApi
+ @TestApi
public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
List<Locale> newLocales, String newServiceId, Date start, Date end,
List<FileInfo> newFiles) {
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index d38d8a712c73..b4ad1d77760a 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -22,6 +22,8 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.*;
import android.content.pm.ServiceInfo;
+import android.telephony.MbmsDownloadSession;
+import android.telephony.MbmsStreamingSession;
import android.util.Log;
import java.io.File;
@@ -48,24 +50,64 @@ public class MbmsUtils {
return new ComponentName(ci.packageName, ci.name);
}
+ private static ComponentName getOverrideServiceName(Context context, String serviceAction) {
+ String metaDataKey = null;
+ switch (serviceAction) {
+ case MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION:
+ metaDataKey = MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA;
+ break;
+ case MbmsStreamingSession.MBMS_STREAMING_SERVICE_ACTION:
+ metaDataKey = MbmsStreamingSession.MBMS_STREAMING_SERVICE_OVERRIDE_METADATA;
+ break;
+ }
+ if (metaDataKey == null) {
+ return null;
+ }
+
+ ApplicationInfo appInfo;
+ try {
+ appInfo = context.getPackageManager()
+ .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ if (appInfo.metaData == null) {
+ return null;
+ }
+ String serviceComponent = appInfo.metaData.getString(metaDataKey);
+ if (serviceComponent == null) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(serviceComponent);
+ }
+
public static ServiceInfo getMiddlewareServiceInfo(Context context, String serviceAction) {
// Query for the proper service
PackageManager packageManager = context.getPackageManager();
Intent queryIntent = new Intent();
queryIntent.setAction(serviceAction);
- List<ResolveInfo> downloadServices = packageManager.queryIntentServices(queryIntent,
- PackageManager.MATCH_SYSTEM_ONLY);
- if (downloadServices == null || downloadServices.size() == 0) {
- Log.w(LOG_TAG, "No download services found, cannot get service info");
+ ComponentName overrideService = getOverrideServiceName(context, serviceAction);
+ List<ResolveInfo> services;
+ if (overrideService == null) {
+ services = packageManager.queryIntentServices(queryIntent,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ } else {
+ queryIntent.setComponent(overrideService);
+ services = packageManager.queryIntentServices(queryIntent,
+ PackageManager.MATCH_ALL);
+ }
+
+ if (services == null || services.size() == 0) {
+ Log.w(LOG_TAG, "No MBMS services found, cannot get service info");
return null;
}
- if (downloadServices.size() > 1) {
- Log.w(LOG_TAG, "More than one download service found, cannot get unique service");
+ if (services.size() > 1) {
+ Log.w(LOG_TAG, "More than one MBMS service found, cannot get unique service");
return null;
}
- return downloadServices.get(0).serviceInfo;
+ return services.get(0).serviceInfo;
}
public static int startBinding(Context context, String serviceAction,
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
index c704f346e311..ef2a14aa26b3 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +42,7 @@ public final class StreamingServiceInfo extends ServiceInfo implements Parcelabl
* @hide
*/
@SystemApi
+ @TestApi
public StreamingServiceInfo(Map<Locale, String> names, String className,
List<Locale> locales, String serviceId, Date start, Date end) {
super(names, className, locales, serviceId, start, end);
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 9ccdd56fd82d..4fee3df83c9a 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -18,6 +18,7 @@ package android.telephony.mbms.vendor;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
@@ -42,6 +43,7 @@ import java.util.Map;
* @hide
*/
@SystemApi
+@TestApi
public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index a2381536ac0c..db177c0c7768 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -18,6 +18,7 @@ package android.telephony.mbms.vendor;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
@@ -38,6 +39,7 @@ import java.util.List;
* @hide
*/
@SystemApi
+@TestApi
public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/**
* Initialize streaming service for this app and subId, registering the listener.
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index fe37531c0611..a4eb424ab66e 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -187,6 +187,57 @@ interface ISms {
in PendingIntent deliveryIntent, in boolean persistMessage);
/**
+ * Send an SMS with options using Subscription Id.
+ *
+ * @param subId the subId on which the SMS has to be sent.
+ * @param destAddr the address to send the message to
+ * @param scAddr the SMSC to send the message through, or NULL for the
+ * default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param persistMessageForNonDefaultSmsApp whether the sent message should
+ * be automatically persisted in the SMS db. It only affects messages sent
+ * by a non-default SMS app. Currently only the carrier app can set this
+ * parameter to false to skip auto message persistence.
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending message is multi segmented or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ */
+ void sendTextForSubscriberWithOptions(in int subId, String callingPkg, in String destAddr,
+ in String scAddr, in String text, in PendingIntent sentIntent,
+ in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp,
+ in int priority, in boolean expectMore, in int validityPeriod);
+
+ /**
* Inject an SMS PDU into the android platform.
*
* @param subId the subId on which the SMS has to be injected.
@@ -234,6 +285,56 @@ interface ISms {
in List<PendingIntent> deliveryIntents, in boolean persistMessageForNonDefaultSmsApp);
/**
+ * Send a multi-part text based SMS with options using Subscription Id.
+ *
+ * @param subId the subId on which the SMS has to be sent.
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code>
+ * <code>RESULT_ERROR_RADIO_OFF</code>
+ * <code>RESULT_ERROR_NULL_PDU</code>.
+ * @param deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ * @param persistMessageForNonDefaultSmsApp whether the sent message should
+ * be automatically persisted in the SMS db. It only affects messages sent
+ * by a non-default SMS app. Currently only the carrier app can set this
+ * parameter to false to skip auto message persistence.
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending message is multi segmented or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ */
+ void sendMultipartTextForSubscriberWithOptions(in int subId, String callingPkg,
+ in String destinationAddress, in String scAddress, in List<String> parts,
+ in List<PendingIntent> sentIntents, in List<PendingIntent> deliveryIntents,
+ in boolean persistMessageForNonDefaultSmsApp, in int priority, in boolean expectMore,
+ in int validityPeriod);
+
+ /**
* Enable reception of cell broadcast (SMS-CB) messages with the given
* message identifier and RAN type. The RAN type specify this message ID
* belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 7a53ef63e2a8..14c5f4bebebb 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -99,6 +99,15 @@ public class SmsMessage extends SmsMessageBase {
private static final int RETURN_NO_ACK = 0;
private static final int RETURN_ACK = 1;
+ /**
+ * Supported priority modes for CDMA SMS messages
+ * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
+ */
+ private static final int PRIORITY_NORMAL = 0x0;
+ private static final int PRIORITY_INTERACTIVE = 0x1;
+ private static final int PRIORITY_URGENT = 0x2;
+ private static final int PRIORITY_EMERGENCY = 0x3;
+
private SmsEnvelope mEnvelope;
private BearerData mBearerData;
@@ -211,6 +220,26 @@ public class SmsMessage extends SmsMessageBase {
*/
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
boolean statusReportRequested, SmsHeader smsHeader) {
+ return getSubmitPdu(scAddr, destAddr, message, statusReportRequested, smsHeader, -1);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddr Service Centre address. Null means use default.
+ * @param destAddr Address of the recipient.
+ * @param message String representation of the message payload.
+ * @param statusReportRequested Indicates whether a report is requested for this message.
+ * @param smsHeader Array containing the data for the User Data Header, preceded
+ * by the Element Identifiers.
+ * @param priority Priority level of the message
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
+ boolean statusReportRequested, SmsHeader smsHeader, int priority) {
/**
* TODO(cleanup): Do we really want silent failure like this?
@@ -224,7 +253,7 @@ public class SmsMessage extends SmsMessageBase {
UserData uData = new UserData();
uData.payloadStr = message;
uData.userDataHeader = smsHeader;
- return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
+ return privateGetSubmitPdu(destAddr, statusReportRequested, uData, priority);
}
/**
@@ -282,6 +311,22 @@ public class SmsMessage extends SmsMessageBase {
}
/**
+ * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+ *
+ * @param destAddr the address of the destination for the message
+ * @param userData the data for the message
+ * @param statusReportRequested Indicates whether a report is requested for this message.
+ * @param priority Priority level of the message
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
+ boolean statusReportRequested, int priority) {
+ return privateGetSubmitPdu(destAddr, statusReportRequested, userData, priority);
+ }
+
+ /**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
@Override
@@ -764,6 +809,15 @@ public class SmsMessage extends SmsMessageBase {
*/
private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
UserData userData) {
+ return privateGetSubmitPdu(destAddrStr, statusReportRequested, userData, -1);
+ }
+
+ /**
+ * Creates BearerData and Envelope from parameters for a Submit SMS.
+ * @return byte stream for SubmitPdu.
+ */
+ private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
+ UserData userData, int priority) {
/**
* TODO(cleanup): give this function a more meaningful name.
@@ -792,6 +846,10 @@ public class SmsMessage extends SmsMessageBase {
bearerData.userAckReq = false;
bearerData.readAckReq = false;
bearerData.reportReq = false;
+ if (priority >= PRIORITY_NORMAL && priority <= PRIORITY_EMERGENCY) {
+ bearerData.priorityIndicatorSet = true;
+ bearerData.priority = priority;
+ }
bearerData.userData = userData;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 1ca19e01d6c8..4f5bfa919135 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -89,6 +89,18 @@ public class SmsMessage extends SmsMessageBase {
private int mVoiceMailCount = 0;
+ private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00;
+ private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01;
+ private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02;
+ private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03;
+
+ //Validity Period min - 5 mins
+ private static final int VALIDITY_PERIOD_MIN = 5;
+ //Validity Period max - 63 weeks
+ private static final int VALIDITY_PERIOD_MAX = 635040;
+
+ private static final int INVALID_VALIDITY_PERIOD = -1;
+
public static class SubmitPdu extends SubmitPduBase {
}
@@ -202,6 +214,45 @@ public class SmsMessage extends SmsMessageBase {
}
/**
+ * Get Encoded Relative Validty Period Value from Validity period in mins.
+ *
+ * @param validityPeriod Validity period in mins.
+ *
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * ||relValidityPeriod (TP-VP) || || validityPeriod ||
+ *
+ * 0 to 143 ---> (TP-VP + 1) x 5 minutes
+ *
+ * 144 to 167 ---> 12 hours + ((TP-VP -143) x 30 minutes)
+ *
+ * 168 to 196 ---> (TP-VP - 166) x 1 day
+ *
+ * 197 to 255 ---> (TP-VP - 192) x 1 week
+ *
+ * @return relValidityPeriod Encoded Relative Validity Period Value.
+ * @hide
+ */
+ public static int getRelativeValidityPeriod(int validityPeriod) {
+ int relValidityPeriod = INVALID_VALIDITY_PERIOD;
+
+ if (validityPeriod < VALIDITY_PERIOD_MIN || validityPeriod > VALIDITY_PERIOD_MAX) {
+ Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod);
+ return relValidityPeriod;
+ }
+
+ if (validityPeriod <= 720) {
+ relValidityPeriod = (validityPeriod / 5) - 1;
+ } else if (validityPeriod <= 1440) {
+ relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
+ } else if (validityPeriod <= 43200) {
+ relValidityPeriod = (validityPeriod / 1440) + 166;
+ } else if (validityPeriod <= 635040) {
+ relValidityPeriod = (validityPeriod / 10080) + 192;
+ }
+ return relValidityPeriod;
+ }
+
+ /**
* Get an SMS-SUBMIT PDU for a destination address and a message
*
* @param scAddress Service Centre address. Null means use default.
@@ -236,6 +287,29 @@ public class SmsMessage extends SmsMessageBase {
String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding,
int languageTable, int languageShiftTable) {
+ return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
+ header, encoding, languageTable, languageShiftTable, -1);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message using the
+ * specified encoding.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param encoding Encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param languageTable
+ * @param languageShiftTable
+ * @param validityPeriod Validity Period of the message in Minutes.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, byte[] header, int encoding,
+ int languageTable, int languageShiftTable, int validityPeriod) {
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
@@ -272,8 +346,19 @@ public class SmsMessage extends SmsMessageBase {
}
SubmitPdu ret = new SubmitPdu();
- // MTI = SMS-SUBMIT, UDHI = header != null
- byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
+
+ int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE;
+ int relativeValidityPeriod = INVALID_VALIDITY_PERIOD;
+
+ // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3
+ //bit 4:3 = 10 - TP-VP field present - relative format
+ if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) {
+ validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE;
+ }
+
+ byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) |
+ (header != null ? 0x40 : 0x00));
+
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, mtiByte,
statusReportRequested, ret);
@@ -338,7 +423,11 @@ public class SmsMessage extends SmsMessageBase {
bo.write(0x08);
}
- // (no TP-Validity-Period)
+ if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
+ // ( TP-Validity-Period - relative format)
+ bo.write(relativeValidityPeriod);
+ }
+
bo.write(userData, 0, userData.length);
ret.encodedMessage = bo.toByteArray();
return ret;
@@ -388,6 +477,24 @@ public class SmsMessage extends SmsMessageBase {
}
/**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message
+ * @param statusReportRequested staus report of the message Requested
+ * @param validityPeriod Validity Period of the message in Minutes.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, int validityPeriod) {
+ return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
+ null, ENCODING_UNKNOWN, 0, 0, validityPeriod);
+ }
+
+ /**
* Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
*
* @param scAddress Service Centre address. null == use default
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 62d570c80043..99a82ad00d25 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -567,4 +567,12 @@ public class IccUtils {
} while (valueIndex < endIndex);
return result;
}
+
+ public static String getDecimalSubstring(String iccId) {
+ int position;
+ for (position = 0; position < iccId.length(); position ++) {
+ if (!Character.isDigit(iccId.charAt(position))) break;
+ }
+ return iccId.substring( 0, position );
+ }
}
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index a5261d049973..95eb5c9e7770 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -17,13 +17,13 @@
package com.android.compatibilitytest;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessErrorStateInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.app.UiModeManager;
-import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -72,6 +72,8 @@ public class AppCompatibility {
DROPBOX_TAGS.add("data_app_native_crash");
DROPBOX_TAGS.add("data_app_crash");
}
+ private static final int MAX_CRASH_SNIPPET_LINES = 20;
+ private static final int MAX_NUM_CRASH_SNIPPET = 3;
// time waiting for app to launch
private int mAppLaunchTimeout = 7000;
@@ -149,11 +151,18 @@ public class AppCompatibility {
try {
checkDropbox(startTime, packageName);
if (mAppErrors.containsKey(packageName)) {
- StringBuilder message = new StringBuilder("Error detected for package: ")
+ StringBuilder message = new StringBuilder("Error(s) detected for package: ")
.append(packageName);
- for (String err : mAppErrors.get(packageName)) {
+ List<String> errors = mAppErrors.get(packageName);
+ for (int i = 0; i < MAX_NUM_CRASH_SNIPPET && i < errors.size(); i++) {
+ String err = errors.get(i);
message.append("\n\n");
- message.append(err);
+ // limit the size of each crash snippet
+ message.append(truncate(err, MAX_CRASH_SNIPPET_LINES));
+ }
+ if (errors.size() > MAX_NUM_CRASH_SNIPPET) {
+ message.append(String.format("\n... %d more errors omitted ...",
+ errors.size() - MAX_NUM_CRASH_SNIPPET));
}
Assert.fail(message.toString());
}
@@ -171,6 +180,28 @@ public class AppCompatibility {
}
/**
+ * Truncate the text to at most the specified number of lines, and append a marker at the end
+ * when truncated
+ * @param text
+ * @param maxLines
+ * @return
+ */
+ private static String truncate(String text, int maxLines) {
+ String[] lines = text.split("\\r?\\n");
+ StringBuilder ret = new StringBuilder();
+ for (int i = 0; i < maxLines && i < lines.length; i++) {
+ ret.append(lines[i]);
+ ret.append('\n');
+ }
+ if (lines.length > maxLines) {
+ ret.append("... ");
+ ret.append(lines.length - maxLines);
+ ret.append(" more lines truncated ...\n");
+ }
+ return ret.toString();
+ }
+
+ /**
* Check dropbox for entries of interest regarding the specified process
* @param startTime if not 0, only check entries with timestamp later than the start time
* @param processName the process name to check for
@@ -255,7 +286,7 @@ public class AppCompatibility {
} else {
errors = new ArrayList<>();
}
- errors.add(String.format("type: %s details:\n%s", errorType, errorInfo));
+ errors.add(String.format("### Type: %s, Details:\n%s", errorType, errorInfo));
mAppErrors.put(pkgName, errors);
}
diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml
index f6006b0a7fa9..6435ad971476 100644
--- a/tests/testables/tests/AndroidManifest.xml
+++ b/tests/testables/tests/AndroidManifest.xml
@@ -15,9 +15,11 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.testables">
+ package="com.android.testables" android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 99b4c31884bb..8b6c524e5c8d 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -147,6 +147,10 @@ bool InlineXmlFormatParser::Consume(IAaptContext* context, xml::XmlResource* doc
} else {
new_doc->root.reset(static_cast<xml::Element*>(child.release()));
new_doc->root->parent = nullptr;
+ // Copy down the namespace declarations
+ new_doc->root->namespace_decls = doc->root->namespace_decls;
+ // Recurse for nested inlines
+ Consume(context, new_doc.get());
}
}
diff --git a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
index d6d734d395b5..2b4ab96d5c3a 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
@@ -141,4 +141,47 @@ TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
EXPECT_THAT(extracted_doc_drawable->root->name, StrEq("vector"));
}
+TEST(InlineXmlFormatParserTest, ExtractNestedXmlResources) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
+ <base_root xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="inline_xml">
+ <inline_root>
+ <aapt:attr name="nested_inline_xml">
+ <nested_inline_root/>
+ </aapt:attr>
+ <aapt:attr name="another_nested_inline_xml">
+ <root/>
+ </aapt:attr>
+ </inline_root>
+ </aapt:attr>
+ <aapt:attr name="turtles">
+ <root1>
+ <aapt:attr name="all">
+ <root2>
+ <aapt:attr name="the">
+ <root3>
+ <aapt:attr name="way">
+ <root4>
+ <aapt:attr name="down">
+ <root5/>
+ </aapt:attr>
+ </root4>
+ </aapt:attr>
+ </root3>
+ </aapt:attr>
+ </root2>
+ </aapt:attr>
+ </root1>
+ </aapt:attr>
+ </base_root>)");
+
+ doc->file.name = test::ParseNameOrDie("layout/main");
+
+ InlineXmlFormatParser parser;
+ ASSERT_TRUE(parser.Consume(context.get(), doc.get()));
+ // Confirm that all of the nested inline xmls are parsed out.
+ ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(8u));
+}
} // namespace aapt
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 135df405c747..674bee139186 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -18,6 +18,7 @@
#include <frameworks/base/core/proto/android/os/incident.pb.h>
#include <map>
+#include <set>
#include <string>
using namespace android;
@@ -27,6 +28,60 @@ using namespace google::protobuf::io;
using namespace google::protobuf::internal;
using namespace std;
+/**
+ * Implementation details:
+ * This binary auto generates .cpp files for incident and incidentd.
+ *
+ * When argument "incident" is specified, it generates incident_section.cpp file.
+ *
+ * When argument "incidentd" is specified, it generates section_list.cpp file.
+ *
+ * In section_list.cpp file, it generates a SECTION_LIST array and a PRIVACY_POLICY_LIST array.
+ * For SECTION_LIST, it generates Section.h classes only for proto fields with section option enabled.
+ * For PRIVACY_POLICY_LIST, it generates Privacy.h classes only for proto fields with privacy option enabled.
+ *
+ * For Privacy struct, it is possible to have self recursion definitions since protobuf is defining "classes"
+ * So the logic to handle it becomes very complicated when Privacy tag of a message contains a list of Privacies
+ * of its sub-messages. The code also handles multiple depth of self recursion fields.
+ *
+ * For example here is a one level self recursion message WindowManager:
+ * message WindowState {
+ * string state = 1 [(privacy).dest = LOCAL];
+ * int32 display_id = 2;
+ * repeated WindowState child_windows = 3;
+ * }
+ *
+ * message WindowManager {
+ * WindowState my_window = 1;
+ * }
+ *
+ * When generating Privacy options for WindowManager, this tool will generate cpp syntax source code:
+ *
+ * #include "section_list.h"
+ * ...
+ * Privacy WindowState_state { 1, 9, NULL, LOCAL, NULL }; // first two integers are values for field id and proto type.
+ * Privacy WindowState_child_windows { 3, 11, NULL, DEFAULT, NULL }; // reserved for WindowState_LIST
+ * Privacy* WindowState_MSG_[] = {
+ * &WindowState_state,
+ * // display id is default, nothing is generated.
+ * &WindowState_child_windows,
+ * NULL // terminator of the array
+ * };
+ * Privacy WindowState_my_window { 1, 11, WindowState_my_window_LIST, DEFAULT, NULL };
+ *
+ * createList() {
+ * ...
+ * WindowState_child_windows.children = WindowState_my_window_LIST; // point to its own definition after the list is defined.
+ * ...
+ * }
+ *
+ * const Privacy** PRIVACY_POLICY_LIST = createList();
+ * const int PRIVACY_POLICY_COUNT = 1;
+ */
+
+// The assignments will be called when constructs PRIVACY_POLICY_LIST, has to be global variable
+vector<string> gSelfRecursionAssignments;
+
static inline void emptyline() {
printf("\n");
}
@@ -38,7 +93,7 @@ static void generateHead(const char* header) {
emptyline();
}
-// ================================================================================
+// ======================== incident_sections =============================
static bool generateIncidentSectionsCpp(Descriptor const* descriptor)
{
generateHead("incident_sections");
@@ -73,7 +128,7 @@ static bool generateIncidentSectionsCpp(Descriptor const* descriptor)
return true;
}
-// ================================================================================
+// ========================= section_list ===================================
static void splitAndPrint(const string& args) {
size_t base = 0;
size_t found;
@@ -88,12 +143,12 @@ static void splitAndPrint(const string& args) {
}
}
-static const std::string replaceAll(const string& field_name, const char oldC, const string& newS) {
- if (field_name.find_first_of(oldC) == field_name.npos) return field_name.c_str();
+static string replaceAll(const string& fieldName, const char oldC, const string& newS) {
+ if (fieldName.find_first_of(oldC) == fieldName.npos) return fieldName.c_str();
size_t pos = 0, idx = 0;
- char* res = new char[field_name.size() * newS.size() + 1]; // assign a larger buffer
- while (pos != field_name.size()) {
- char cur = field_name[pos++];
+ char* res = new char[fieldName.size() * newS.size() + 1]; // assign a larger buffer
+ while (pos != fieldName.size()) {
+ char cur = fieldName[pos++];
if (cur != oldC) {
res[idx++] = cur;
continue;
@@ -104,92 +159,162 @@ static const std::string replaceAll(const string& field_name, const char oldC, c
}
}
res[idx] = '\0';
- std::string result(res);
+ string result(res);
delete [] res;
return result;
}
-static inline bool isDefaultDest(const FieldDescriptor* field) {
- return field->options().GetExtension(privacy).dest() == PrivacyFlags::default_instance().dest();
+static string getFieldName(const FieldDescriptor* field) {
+ return replaceAll(field->full_name(), '.', "__");
+}
+
+static string getMessageTypeName(const Descriptor* descriptor) {
+ return replaceAll(descriptor->full_name(), '.', "_") + "_MSG_";
+}
+
+static inline SectionFlags getSectionFlags(const FieldDescriptor* field) {
+ return field->options().GetExtension(section);
}
+static inline PrivacyFlags getPrivacyFlags(const FieldDescriptor* field) {
+ return field->options().GetExtension(privacy);
+}
+
+static inline bool isDefaultField(const FieldDescriptor* field) {
+ return getPrivacyFlags(field).dest() == PrivacyFlags::default_instance().dest();
+}
+
+static bool isDefaultMessageImpl(const Descriptor* descriptor, set<string>* parents) {
+ int N = descriptor->field_count();
+ parents->insert(descriptor->full_name());
+ for (int i=0; i<N; ++i) {
+ const FieldDescriptor* field = descriptor->field(i);
+ // look at if the current field is default or not, return false immediately
+ if (!isDefaultField(field)) return false;
+
+ switch (field->type()) {
+ case FieldDescriptor::TYPE_MESSAGE:
+ // if self recursion, don't go deep.
+ if (parents->find(field->message_type()->full_name()) != parents->end()) break;
+ // if is a default message, just continue
+ if (isDefaultMessageImpl(field->message_type(), parents)) break;
+ // sub message is not default, so this message is always not default
+ return false;
+ case FieldDescriptor::TYPE_STRING:
+ if (getPrivacyFlags(field).patterns_size() != 0) return false;
+ default:
+ continue;
+ }
+ }
+ parents->erase(descriptor->full_name());
+ return true;
+}
+
+static bool isDefaultMessage(const Descriptor* descriptor) {
+ set<string> parents;
+ return isDefaultMessageImpl(descriptor, &parents);
+}
+
+// This function is called for looking at privacy tags for a message type and recursively its sub-messages
+// It prints out each fields's privacy tags and a List of Privacy of the message itself (don't print default values)
// Returns false if the descriptor doesn't have any non default privacy flags set, including its submessages
-static bool generatePrivacyFlags(const Descriptor* descriptor, const char* alias, map<string, bool> &msgNames) {
+static bool generatePrivacyFlags(const Descriptor* descriptor, map<string, bool> &msgNames, set<string>* parents) {
bool hasDefaultFlags[descriptor->field_count()];
+
+ string messageTypeName = getMessageTypeName(descriptor);
+ // if the message is already defined, skip it.
+ if (msgNames.find(messageTypeName) != msgNames.end()) {
+ bool hasDefault = msgNames[messageTypeName];
+ return !hasDefault; // don't generate if it has default privacy.
+ }
+ // insert the message type name so sub-message will figure out if self-recursion occurs
+ parents->insert(messageTypeName);
+
// iterate though its field and generate sub flags first
for (int i=0; i<descriptor->field_count(); i++) {
hasDefaultFlags[i] = true; // set default to true
+
const FieldDescriptor* field = descriptor->field(i);
- const std::string field_name_str = replaceAll(field->full_name(), '.', "__");
- const char* field_name = field_name_str.c_str();
- // check if the same name is already defined
- if (msgNames.find(field_name) != msgNames.end()) {
- hasDefaultFlags[i] = msgNames[field_name];
+ const string fieldName = getFieldName(field);
+ // check if the same field name is already defined.
+ if (msgNames.find(fieldName) != msgNames.end()) {
+ hasDefaultFlags[i] = msgNames[fieldName];
continue;
};
- PrivacyFlags p = field->options().GetExtension(privacy);
+ PrivacyFlags p = getPrivacyFlags(field);
+ string fieldMessageName;
switch (field->type()) {
case FieldDescriptor::TYPE_MESSAGE:
- if (generatePrivacyFlags(field->message_type(), field_name, msgNames)) {
- printf("Privacy %s { %d, %d, %s_LIST, %d, NULL };\n", field_name, field->number(),
- field->type(), field_name, p.dest());
- } else if (isDefaultDest(field)) {
+ fieldMessageName = getMessageTypeName(field->message_type());
+ if (parents->find(fieldMessageName) != parents->end()) { // Self-Recursion proto definition
+ if (isDefaultField(field)) {
+ hasDefaultFlags[i] = isDefaultMessage(field->message_type());
+ } else {
+ hasDefaultFlags[i] = false;
+ }
+ if (!hasDefaultFlags[i]) {
+ printf("Privacy %s = { %d, %d, NULL, %d, NULL }; // self recursion field of %s\n",
+ fieldName.c_str(), field->number(), field->type(), p.dest(), fieldMessageName.c_str());
+ // generate the assignment and used to construct createList function later on.
+ gSelfRecursionAssignments.push_back(fieldName + ".children = " + fieldMessageName);
+ }
+ break;
+ } else if (generatePrivacyFlags(field->message_type(), msgNames, parents)) {
+ printf("Privacy %s = { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(),
+ field->type(), fieldMessageName.c_str(), p.dest());
+ } else if (isDefaultField(field)) {
// don't create a new privacy if the value is default.
break;
- } else{
- printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", field_name, field->number(),
+ } else {
+ printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
field->type(), p.dest());
}
hasDefaultFlags[i] = false;
break;
case FieldDescriptor::TYPE_STRING:
- if (isDefaultDest(field) && p.patterns_size() == 0) break;
+ if (isDefaultField(field) && p.patterns_size() == 0) break;
- printf("const char* %s_patterns[] = {\n", field_name);
+ printf("const char* %s_patterns[] = {\n", fieldName.c_str());
for (int i=0; i<p.patterns_size(); i++) {
// the generated string need to escape backslash as well, need to dup it here
printf(" \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str());
}
printf(" NULL };\n");
- printf("Privacy %s { %d, %d, NULL, %d, %s_patterns };\n", field_name, field->number(),
- field->type(), p.dest(), field_name);
+ printf("Privacy %s = { %d, %d, NULL, %d, %s_patterns };\n", fieldName.c_str(), field->number(),
+ field->type(), p.dest(), fieldName.c_str());
hasDefaultFlags[i] = false;
break;
default:
- if (isDefaultDest(field)) break;
- printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", field_name, field->number(),
+ if (isDefaultField(field)) break;
+ printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
field->type(), p.dest());
hasDefaultFlags[i] = false;
}
// add the field name to message map, true means it has default flags
- msgNames[field_name] = hasDefaultFlags[i];
+ msgNames[fieldName] = hasDefaultFlags[i];
}
bool allDefaults = true;
for (int i=0; i<descriptor->field_count(); i++) {
allDefaults &= hasDefaultFlags[i];
}
+
+ parents->erase(messageTypeName); // erase the message type name when exit the message.
+ msgNames[messageTypeName] = allDefaults; // store the privacy tags of the message here to avoid overhead.
+
if (allDefaults) return false;
emptyline();
-
- bool needConst = strcmp(alias, "PRIVACY_POLICY") == 0;
int policyCount = 0;
-
- printf("%sPrivacy* %s_LIST[] = {\n", needConst ? "const " : "", alias);
+ printf("Privacy* %s[] = {\n", messageTypeName.c_str());
for (int i=0; i<descriptor->field_count(); i++) {
const FieldDescriptor* field = descriptor->field(i);
if (hasDefaultFlags[i]) continue;
- printf(" &%s,\n", replaceAll(field->full_name(), '.', "__").c_str());
+ printf(" &%s,\n", getFieldName(field).c_str());
policyCount++;
}
- if (needConst) {
- printf("};\n\n");
- printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
- } else {
- printf(" NULL };\n");
- }
+ printf(" NULL };\n");
emptyline();
return true;
}
@@ -198,6 +323,8 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
generateHead("section_list");
// generates SECTION_LIST
+ printf("// Generate SECTION_LIST.\n\n");
+
printf("const Section* SECTION_LIST[] = {\n");
for (int i=0; i<descriptor->field_count(); i++) {
const FieldDescriptor* field = descriptor->field(i);
@@ -205,7 +332,7 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
continue;
}
- const SectionFlags s = field->options().GetExtension(section);
+ const SectionFlags s = getSectionFlags(field);
switch (s.type()) {
case SECTION_NONE:
continue;
@@ -225,16 +352,73 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
}
}
printf(" NULL };\n");
+
+ emptyline();
+ printf("// =============================================================================\n");
emptyline();
- // generates PRIVACY_POLICY
+ // generates PRIVACY_POLICY_LIST
+ printf("// Generate PRIVACY_POLICY_LIST.\n\n");
map<string, bool> messageNames;
- if (!generatePrivacyFlags(descriptor, "PRIVACY_POLICY", messageNames)) {
- // if no privacy options set at all, define an empty list
- printf("const Privacy* PRIVACY_POLICY_LIST[] = {};\n");
- printf("const int PRIVACY_POLICY_COUNT = 0;\n");
+ set<string> parents;
+ bool skip[descriptor->field_count()];
+
+ for (int i=0; i<descriptor->field_count(); i++) {
+ const FieldDescriptor* field = descriptor->field(i);
+ const string fieldName = getFieldName(field);
+ PrivacyFlags p = getPrivacyFlags(field);
+
+ skip[i] = true;
+
+ if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+ continue;
+ }
+ // generate privacy flags for each field.
+ if (generatePrivacyFlags(field->message_type(), messageNames, &parents)) {
+ printf("Privacy %s { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(),
+ field->type(), getMessageTypeName(field->message_type()).c_str(), p.dest());
+ } else if (isDefaultField(field)) {
+ continue; // don't create a new privacy if the value is default.
+ } else {
+ printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
+ field->type(), p.dest());
+ }
+ skip[i] = false;
}
+ // generate final PRIVACY_POLICY_LIST
+ emptyline();
+ int policyCount = 0;
+ if (gSelfRecursionAssignments.empty()) {
+ printf("Privacy* privacyArray[] = {\n");
+ for (int i=0; i<descriptor->field_count(); i++) {
+ if (skip[i]) continue;
+ printf(" &%s,\n", getFieldName(descriptor->field(i)).c_str());
+ policyCount++;
+ }
+ printf("};\n\n");
+ printf("const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(privacyArray);\n\n");
+ printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
+ } else {
+ for (int i=0; i<descriptor->field_count(); i++) {
+ if (!skip[i]) policyCount++;
+ }
+
+ printf("static const Privacy** createList() {\n");
+ for (size_t i=0; i<gSelfRecursionAssignments.size(); ++i) {
+ printf(" %s;\n", gSelfRecursionAssignments[i].c_str());
+ }
+ printf(" Privacy** privacyArray = (Privacy**)malloc(%d * sizeof(Privacy**));\n", policyCount);
+ policyCount = 0; // reset
+ for (int i=0; i<descriptor->field_count(); i++) {
+ if (skip[i]) continue;
+ printf(" privacyArray[%d] = &%s;\n", policyCount++, getFieldName(descriptor->field(i)).c_str());
+ }
+ printf(" return const_cast<const Privacy**>(privacyArray);\n");
+ printf("}\n\n");
+ printf("const Privacy** PRIVACY_POLICY_LIST = createList();\n\n");
+ printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
+ }
return true;
}
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 5d2926821164..f76196dd2a19 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -22,6 +22,7 @@
namespace android {
namespace stats_log_api_gen {
+using google::protobuf::EnumDescriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::FileDescriptor;
using google::protobuf::SourceLocation;
@@ -120,7 +121,7 @@ java_type(const FieldDescriptor* field)
case FieldDescriptor::TYPE_UINT32:
return JAVA_TYPE_INT;
case FieldDescriptor::TYPE_ENUM:
- return JAVA_TYPE_INT;
+ return JAVA_TYPE_ENUM;
case FieldDescriptor::TYPE_SFIXED32:
return JAVA_TYPE_INT;
case FieldDescriptor::TYPE_SFIXED64:
@@ -208,7 +209,6 @@ collate_atoms(const Descriptor* descriptor, Atoms* atoms)
errorCount++;
continue;
}
-
}
// Check that if there's a WorkSource, it's at position 1.
@@ -228,15 +228,26 @@ collate_atoms(const Descriptor* descriptor, Atoms* atoms)
AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
- // Build the type signature
+ // Build the type signature and the atom data.
vector<java_type_t> signature;
for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
it != fields.end(); it++) {
const FieldDescriptor* field = it->second;
java_type_t javaType = java_type(field);
- atomDecl.fields.push_back(AtomField(field->name(), javaType));
- signature.push_back(javaType);
+ AtomField atField(field->name(), javaType);
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature.push_back(JAVA_TYPE_INT);
+ const EnumDescriptor* enumDescriptor = field->enum_type();
+ for (int i = 0; i < enumDescriptor->value_count(); i++) {
+ atField.enumValues[enumDescriptor->value(i)->number()] =
+ enumDescriptor->value(i)->name().c_str();
+ }
+ } else {
+ signature.push_back(javaType);
+ }
+ atomDecl.fields.push_back(atField);
}
atoms->signatures.insert(signature);
@@ -261,5 +272,3 @@ collate_atoms(const Descriptor* descriptor, Atoms* atoms)
} // namespace stats_log_api_gen
} // namespace android
-
-
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 50af7ea2d335..2f840d7f4469 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -22,10 +22,12 @@
#include <set>
#include <vector>
+#include <map>
namespace android {
namespace stats_log_api_gen {
+using std::map;
using std::set;
using std::string;
using std::vector;
@@ -44,6 +46,7 @@ typedef enum {
JAVA_TYPE_FLOAT = 5,
JAVA_TYPE_DOUBLE = 6,
JAVA_TYPE_STRING = 7,
+ JAVA_TYPE_ENUM = 8,
JAVA_TYPE_OBJECT = -1,
JAVA_TYPE_BYTE_ARRAY = -2,
@@ -57,8 +60,13 @@ struct AtomField {
string name;
java_type_t javaType;
+ // If the field is of type enum, the following map contains the list of enum values.
+ map<int /* numeric value */, string /* value name */> enumValues;
+
inline AtomField() :name(), javaType(JAVA_TYPE_UNKNOWN) {}
- inline AtomField(const AtomField& that) :name(that.name), javaType(that.javaType) {}
+ inline AtomField(const AtomField& that) :name(that.name),
+ javaType(that.javaType),
+ enumValues(that.enumValues) {}
inline AtomField(string n, java_type_t jt) :name(n), javaType(jt) {}
inline ~AtomField() {}
};
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index aaea4f6fe749..6350b725f09a 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -56,6 +56,7 @@ cpp_type_name(java_type_t type)
case JAVA_TYPE_BOOLEAN:
return "bool";
case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
return "int32_t";
case JAVA_TYPE_LONG:
return "int64_t";
@@ -77,6 +78,7 @@ java_type_name(java_type_t type)
case JAVA_TYPE_BOOLEAN:
return "boolean";
case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
return "int";
case JAVA_TYPE_LONG:
return "long";
@@ -173,7 +175,7 @@ write_stats_log_header(FILE* out, const Atoms& atoms)
fprintf(out, " */\n");
fprintf(out, "\n");
fprintf(out, "/**\n");
- fprintf(out, " * Constants for event codes.\n");
+ fprintf(out, " * Constants for atom codes.\n");
fprintf(out, " */\n");
fprintf(out, "enum {\n");
@@ -240,9 +242,9 @@ write_stats_log_java(FILE* out, const Atoms& atoms)
fprintf(out, " * @hide\n");
fprintf(out, " */\n");
fprintf(out, "public final class StatsLog {\n");
- fprintf(out, " // Constants for event codes.\n");
+ fprintf(out, " // Constants for atom codes.\n");
- // Print constants
+ // Print constants for the atom codes.
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
atom != atoms.decls.end(); atom++) {
string constant = make_constant_name(atom->name);
@@ -260,6 +262,27 @@ write_stats_log_java(FILE* out, const Atoms& atoms)
}
fprintf(out, "\n");
+ // Print constants for the enum values.
+ fprintf(out, " // Constants for enum values.\n\n");
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ for (vector<AtomField>::const_iterator field = atom->fields.begin();
+ field != atom->fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ENUM) {
+ fprintf(out, " // Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
+ for (map<int, string>::const_iterator value = field->enumValues.begin();
+ value != field->enumValues.end(); value++) {
+ fprintf(out, " public static final int %s__%s__%s = %d;\n",
+ make_constant_name(atom->message).c_str(),
+ make_constant_name(field->name).c_str(),
+ make_constant_name(value->second).c_str(),
+ value->first);
+ }
+ fprintf(out, "\n");
+ }
+ }
+ }
+
// Print write methods
fprintf(out, " // Write methods\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
@@ -286,6 +309,7 @@ jni_type_name(java_type_t type)
case JAVA_TYPE_BOOLEAN:
return "jboolean";
case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
return "jint";
case JAVA_TYPE_LONG:
return "jlong";
@@ -311,6 +335,7 @@ jni_function_name(const vector<java_type_t>& signature)
result += "_boolean";
break;
case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
result += "_int";
break;
case JAVA_TYPE_LONG:
@@ -340,6 +365,7 @@ java_type_signature(java_type_t type)
case JAVA_TYPE_BOOLEAN:
return "Z";
case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
return "I";
case JAVA_TYPE_LONG:
return "J";
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 1bd2e3de332e..073f2cfce6a8 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -24,6 +24,7 @@
namespace android {
namespace stats_log_api_gen {
+using std::map;
using std::set;
using std::vector;
@@ -54,6 +55,29 @@ set_contains_vector(const set<vector<java_type_t>>& s, int count, ...)
EXPECT_TRUE(set_contains_vector(s, count, __VA_ARGS__)); \
} while(0)
+/** Expects that the provided atom has no enum values for any field. */
+#define EXPECT_NO_ENUM_FIELD(atom) \
+ do { \
+ for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
+ field != atom->fields.end(); field++) { \
+ EXPECT_TRUE(field->enumValues.empty()); \
+ } \
+ } while(0)
+
+/** Expects that exactly one specific field has expected enum values. */
+#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values) \
+ do { \
+ for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
+ field != atom->fields.end(); field++) { \
+ if (field->name == field_name) { \
+ EXPECT_EQ(field->enumValues, values); \
+ } else { \
+ EXPECT_TRUE(field->enumValues.empty()); \
+ } \
+ } \
+ } while(0)
+
+
/**
* Test a correct collation, with all the types.
*/
@@ -94,21 +118,28 @@ TEST(CollationTest, CollateStats) {
EXPECT_EQ(1, atom->code);
EXPECT_EQ("int_atom", atom->name);
EXPECT_EQ("IntAtom", atom->message);
+ EXPECT_NO_ENUM_FIELD(atom);
atom++;
EXPECT_EQ(2, atom->code);
EXPECT_EQ("out_of_order_atom", atom->name);
EXPECT_EQ("OutOfOrderAtom", atom->message);
+ EXPECT_NO_ENUM_FIELD(atom);
atom++;
EXPECT_EQ(3, atom->code);
EXPECT_EQ("another_int_atom", atom->name);
EXPECT_EQ("AnotherIntAtom", atom->message);
+ EXPECT_NO_ENUM_FIELD(atom);
atom++;
EXPECT_EQ(4, atom->code);
EXPECT_EQ("all_types_atom", atom->name);
EXPECT_EQ("AllTypesAtom", atom->message);
+ map<int, string> enumValues;
+ enumValues[0] = "VALUE0";
+ enumValues[1] = "VALUE1";
+ EXPECT_HAS_ENUM_FIELD(atom, "enum_field", enumValues);
atom++;
EXPECT_TRUE(atom == atoms.decls.end());
@@ -125,7 +156,7 @@ TEST(CollationTest, NonMessageTypeFails) {
}
/**
- * Test that atoms that have non-primitve types are rejected.
+ * Test that atoms that have non-primitive types are rejected.
*/
TEST(CollationTest, FailOnBadTypes) {
Atoms atoms;
@@ -165,7 +196,5 @@ TEST(CollationTest, FailBadWorkSourcePosition) {
EXPECT_EQ(1, errorCount);
}
-
} // namespace stats_log_api_gen
} // namespace android
-
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 756549c5e880..dc5c14e32cbd 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -21,10 +21,32 @@ cc_defaults {
name: "protoc-gen-stream-defaults",
srcs: [
"Errors.cpp",
+ "stream_proto_utils.cpp",
"string_utils.cpp",
],
+
+ shared_libs: ["libprotoc"],
}
+cc_library {
+ name: "streamingflags",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ target: {
+ host: {
+ proto: {
+ type: "full",
+ },
+ srcs: [
+ "stream.proto",
+ ],
+ },
+ },
+}
cc_binary_host {
name: "protoc-gen-javastream",
@@ -33,7 +55,6 @@ cc_binary_host {
],
defaults: ["protoc-gen-stream-defaults"],
- shared_libs: ["libprotoc"],
}
cc_binary_host {
@@ -43,5 +64,5 @@ cc_binary_host {
],
defaults: ["protoc-gen-stream-defaults"],
- shared_libs: ["libprotoc"],
+ static_libs: ["streamingflags"],
}
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index d4e1b7aede92..481698432711 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -1,106 +1,23 @@
#include "Errors.h"
+#include "stream_proto_utils.h"
#include "string_utils.h"
-#include "google/protobuf/compiler/plugin.pb.h"
-#include "google/protobuf/io/zero_copy_stream_impl.h"
-#include "google/protobuf/text_format.h"
+#include <frameworks/base/tools/streaming_proto/stream.pb.h>
#include <iomanip>
#include <iostream>
#include <sstream>
using namespace android::stream_proto;
-using namespace google::protobuf;
-using namespace google::protobuf::compiler;
using namespace google::protobuf::io;
using namespace std;
-/**
- * Position of the field type in a (long long) fieldId.
- */
-const uint64_t FIELD_TYPE_SHIFT = 32;
-
-//
-// FieldId flags for whether the field is single, repeated or packed.
-// TODO: packed is not supported yet.
-//
-const uint64_t FIELD_COUNT_SHIFT = 40;
-const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_UNKNOWN = 0;
-const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
-
-// Indent
-const string INDENT = " ";
-
-/**
- * See if this is the file for this request, and not one of the imported ones.
- */
-static bool
-should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
-{
- const int N = request.file_to_generate_size();
- for (int i=0; i<N; i++) {
- if (request.file_to_generate(i) == file) {
- return true;
- }
- }
- return false;
-}
-
static string
make_filename(const FileDescriptorProto& file_descriptor)
{
return file_descriptor.name() + ".h";
}
-static string
-get_proto_type(const FieldDescriptorProto& field)
-{
- switch (field.type()) {
- case FieldDescriptorProto::TYPE_DOUBLE:
- return "double";
- case FieldDescriptorProto::TYPE_FLOAT:
- return "float";
- case FieldDescriptorProto::TYPE_INT64:
- return "int64";
- case FieldDescriptorProto::TYPE_UINT64:
- return "uint64";
- case FieldDescriptorProto::TYPE_INT32:
- return "int32";
- case FieldDescriptorProto::TYPE_FIXED64:
- return "fixed64";
- case FieldDescriptorProto::TYPE_FIXED32:
- return "fixed32";
- case FieldDescriptorProto::TYPE_BOOL:
- return "bool";
- case FieldDescriptorProto::TYPE_STRING:
- return "string";
- case FieldDescriptorProto::TYPE_GROUP:
- return "group<unsupported!>";
- case FieldDescriptorProto::TYPE_MESSAGE:
- return field.type_name();
- case FieldDescriptorProto::TYPE_BYTES:
- return "bytes";
- case FieldDescriptorProto::TYPE_UINT32:
- return "uint32";
- case FieldDescriptorProto::TYPE_ENUM:
- return field.type_name();
- case FieldDescriptorProto::TYPE_SFIXED32:
- return "sfixed32";
- case FieldDescriptorProto::TYPE_SFIXED64:
- return "sfixed64";
- case FieldDescriptorProto::TYPE_SINT32:
- return "sint32";
- case FieldDescriptorProto::TYPE_SINT64:
- return "sint64";
- default:
- // won't happen
- return "void";
- }
-}
-
static void
write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
{
@@ -115,27 +32,6 @@ write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& ind
text << endl;
}
-static uint64_t
-get_field_id(const FieldDescriptorProto& field)
-{
- // Number
- uint64_t result = (uint64_t)field.number();
-
- // Type
- result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT;
-
- // Count
- if (field.options().packed()) {
- result |= FIELD_COUNT_PACKED;
- } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
- result |= FIELD_COUNT_REPEATED;
- } else {
- result |= FIELD_COUNT_SINGLE;
- }
-
- return result;
-}
-
static void
write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
{
@@ -160,6 +56,12 @@ write_field(stringstream& text, const FieldDescriptorProto& field, const string&
text << endl;
}
+static inline bool
+should_generate_fields_mapping(const DescriptorProto& message)
+{
+ return message.options().GetExtension(stream).enable_fields_mapping();
+}
+
static void
write_message(stringstream& text, const DescriptorProto& message, const string& indent)
{
@@ -167,8 +69,7 @@ write_message(stringstream& text, const DescriptorProto& message, const string&
const string indented = indent + INDENT;
text << indent << "// message " << message.name() << endl;
- text << indent << "class " << message.name() << " {" << endl;
- text << indent << "public:" << endl;
+ text << indent << "namespace " << message.name() << " {" << endl;
// Enums
N = message.enum_type_size();
@@ -188,12 +89,27 @@ write_message(stringstream& text, const DescriptorProto& message, const string&
write_field(text, message.field(i), indented);
}
- text << indent << "};" << endl;
+ if (should_generate_fields_mapping(message)) {
+ N = message.field_size();
+ text << indented << "const int _FIELD_COUNT = " << N << ";" << endl;
+ text << indented << "const char* _FIELD_NAMES[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indented << INDENT << "\"" << message.field(i).name() << "\"," << endl;
+ }
+ text << indented << "};" << endl;
+ text << indented << "const uint64_t _FIELD_IDS[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indented << INDENT << make_constant_name(message.field(i).name()) << "," << endl;
+ }
+ text << indented << "};" << endl << endl;
+ }
+
+ text << indent << "} //" << message.name() << endl;
text << endl;
}
static void
-write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+write_header_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
{
stringstream text;
@@ -255,7 +171,7 @@ int main(int argc, char const *argv[])
for (int i=0; i<N; i++) {
const FileDescriptorProto& file_descriptor = request.proto_file(i);
if (should_generate_for_file(request, file_descriptor.name())) {
- write_cpp_file(&response, file_descriptor);
+ write_header_file(&response, file_descriptor);
}
}
@@ -270,4 +186,4 @@ int main(int argc, char const *argv[])
/* code */
return 0;
-} \ No newline at end of file
+}
diff --git a/tools/streaming_proto/java/main.cpp b/tools/streaming_proto/java/main.cpp
index b7d594bb9465..c9c50a561a04 100644
--- a/tools/streaming_proto/java/main.cpp
+++ b/tools/streaming_proto/java/main.cpp
@@ -1,11 +1,7 @@
#include "Errors.h"
-
+#include "stream_proto_utils.h"
#include "string_utils.h"
-#include "google/protobuf/compiler/plugin.pb.h"
-#include "google/protobuf/io/zero_copy_stream_impl.h"
-#include "google/protobuf/text_format.h"
-
#include <stdio.h>
#include <iomanip>
#include <iostream>
@@ -13,51 +9,9 @@
#include <map>
using namespace android::stream_proto;
-using namespace google::protobuf;
-using namespace google::protobuf::compiler;
using namespace google::protobuf::io;
using namespace std;
-const int FIELD_TYPE_SHIFT = 32;
-const uint64_t FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_INT32 = 3L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_INT64 = 4L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_UINT32 = 5L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_UINT64 = 6L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_SINT32 = 7L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_SINT64 = 8L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_FIXED32 = 9L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_FIXED64 = 10L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_SFIXED32 = 11L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_SFIXED64 = 12L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_BOOL = 13L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_STRING = 14L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_BYTES = 15L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_ENUM = 16L << FIELD_TYPE_SHIFT;
-const uint64_t FIELD_TYPE_OBJECT = 17L << FIELD_TYPE_SHIFT;
-
-const int FIELD_COUNT_SHIFT = 40;
-const uint64_t FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT;
-
-
-/**
- * See if this is the file for this request, and not one of the imported ones.
- */
-static bool
-should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
-{
- const int N = request.file_to_generate_size();
- for (int i=0; i<N; i++) {
- if (request.file_to_generate(i) == file) {
- return true;
- }
- }
- return false;
-}
-
/**
* If the descriptor gives us a class name, use that. Otherwise make one up from
* the filename of the .proto file.
@@ -112,7 +66,7 @@ make_file_name(const FileDescriptorProto& file_descriptor, const string& class_n
static string
indent_more(const string& indent)
{
- return indent + " ";
+ return indent + INDENT;
}
/**
@@ -133,130 +87,6 @@ write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& ind
}
/**
- * Get the string name for a field.
- */
-static string
-get_proto_type(const FieldDescriptorProto& field)
-{
- switch (field.type()) {
- case FieldDescriptorProto::TYPE_DOUBLE:
- return "double";
- case FieldDescriptorProto::TYPE_FLOAT:
- return "float";
- case FieldDescriptorProto::TYPE_INT64:
- return "int64";
- case FieldDescriptorProto::TYPE_UINT64:
- return "uint64";
- case FieldDescriptorProto::TYPE_INT32:
- return "int32";
- case FieldDescriptorProto::TYPE_FIXED64:
- return "fixed64";
- case FieldDescriptorProto::TYPE_FIXED32:
- return "fixed32";
- case FieldDescriptorProto::TYPE_BOOL:
- return "bool";
- case FieldDescriptorProto::TYPE_STRING:
- return "string";
- case FieldDescriptorProto::TYPE_GROUP:
- return "group<unsupported!>";
- case FieldDescriptorProto::TYPE_MESSAGE:
- return field.type_name();
- case FieldDescriptorProto::TYPE_BYTES:
- return "bytes";
- case FieldDescriptorProto::TYPE_UINT32:
- return "uint32";
- case FieldDescriptorProto::TYPE_ENUM:
- return field.type_name();
- case FieldDescriptorProto::TYPE_SFIXED32:
- return "sfixed32";
- case FieldDescriptorProto::TYPE_SFIXED64:
- return "sfixed64";
- case FieldDescriptorProto::TYPE_SINT32:
- return "sint32";
- case FieldDescriptorProto::TYPE_SINT64:
- return "sint64";
- default:
- // won't happen
- return "void";
- }
-}
-
-static uint64_t
-get_field_id(const FieldDescriptorProto& field)
-{
- // Number
- uint64_t result = (uint32_t)field.number();
-
- // Type
- switch (field.type()) {
- case FieldDescriptorProto::TYPE_DOUBLE:
- result |= FIELD_TYPE_DOUBLE;
- break;
- case FieldDescriptorProto::TYPE_FLOAT:
- result |= FIELD_TYPE_FLOAT;
- break;
- case FieldDescriptorProto::TYPE_INT64:
- result |= FIELD_TYPE_INT64;
- break;
- case FieldDescriptorProto::TYPE_UINT64:
- result |= FIELD_TYPE_UINT64;
- break;
- case FieldDescriptorProto::TYPE_INT32:
- result |= FIELD_TYPE_INT32;
- break;
- case FieldDescriptorProto::TYPE_FIXED64:
- result |= FIELD_TYPE_FIXED64;
- break;
- case FieldDescriptorProto::TYPE_FIXED32:
- result |= FIELD_TYPE_FIXED32;
- break;
- case FieldDescriptorProto::TYPE_BOOL:
- result |= FIELD_TYPE_BOOL;
- break;
- case FieldDescriptorProto::TYPE_STRING:
- result |= FIELD_TYPE_STRING;
- break;
- case FieldDescriptorProto::TYPE_MESSAGE:
- result |= FIELD_TYPE_OBJECT;
- break;
- case FieldDescriptorProto::TYPE_BYTES:
- result |= FIELD_TYPE_BYTES;
- break;
- case FieldDescriptorProto::TYPE_UINT32:
- result |= FIELD_TYPE_UINT32;
- break;
- case FieldDescriptorProto::TYPE_ENUM:
- result |= FIELD_TYPE_ENUM;
- break;
- case FieldDescriptorProto::TYPE_SFIXED32:
- result |= FIELD_TYPE_SFIXED32;
- break;
- case FieldDescriptorProto::TYPE_SFIXED64:
- result |= FIELD_TYPE_SFIXED64;
- break;
- case FieldDescriptorProto::TYPE_SINT32:
- result |= FIELD_TYPE_SINT32;
- break;
- case FieldDescriptorProto::TYPE_SINT64:
- result |= FIELD_TYPE_SINT64;
- break;
- default:
- ;
- }
-
- // Count
- if (field.options().packed()) {
- result |= FIELD_COUNT_PACKED;
- } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
- result |= FIELD_COUNT_REPEATED;
- } else {
- result |= FIELD_COUNT_SINGLE;
- }
-
- return result;
-}
-
-/**
* Write a field.
*/
static void
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
new file mode 100644
index 000000000000..123506c03cfd
--- /dev/null
+++ b/tools/streaming_proto/stream.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+import "google/protobuf/descriptor.proto";
+
+package android.stream_proto;
+
+// This option tells streaming proto plugin to compile .proto files with extra features.
+message StreamFlags {
+ // creates a mapping of field names of the message to its field ids
+ optional bool enable_fields_mapping = 1;
+}
+
+extend google.protobuf.MessageOptions {
+ // Flags used by streaming proto plugins
+ optional StreamFlags stream = 126856794;
+}
diff --git a/tools/streaming_proto/stream_proto_utils.cpp b/tools/streaming_proto/stream_proto_utils.cpp
new file mode 100644
index 000000000000..e8f86bc0be83
--- /dev/null
+++ b/tools/streaming_proto/stream_proto_utils.cpp
@@ -0,0 +1,102 @@
+#include "stream_proto_utils.h"
+
+namespace android {
+namespace stream_proto {
+
+/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 5ULL << FIELD_COUNT_SHIFT;
+
+uint64_t
+get_field_id(const FieldDescriptorProto& field)
+{
+ // Number
+ uint64_t result = (uint32_t)field.number();
+
+ // Type
+ result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT;
+
+ // Count
+ if (field.options().packed()) {
+ result |= FIELD_COUNT_PACKED;
+ } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
+ result |= FIELD_COUNT_REPEATED;
+ } else {
+ result |= FIELD_COUNT_SINGLE;
+ }
+
+ return result;
+}
+
+string
+get_proto_type(const FieldDescriptorProto& field)
+{
+ switch (field.type()) {
+ case FieldDescriptorProto::TYPE_DOUBLE:
+ return "double";
+ case FieldDescriptorProto::TYPE_FLOAT:
+ return "float";
+ case FieldDescriptorProto::TYPE_INT64:
+ return "int64";
+ case FieldDescriptorProto::TYPE_UINT64:
+ return "uint64";
+ case FieldDescriptorProto::TYPE_INT32:
+ return "int32";
+ case FieldDescriptorProto::TYPE_FIXED64:
+ return "fixed64";
+ case FieldDescriptorProto::TYPE_FIXED32:
+ return "fixed32";
+ case FieldDescriptorProto::TYPE_BOOL:
+ return "bool";
+ case FieldDescriptorProto::TYPE_STRING:
+ return "string";
+ case FieldDescriptorProto::TYPE_GROUP:
+ return "group<unsupported!>";
+ case FieldDescriptorProto::TYPE_MESSAGE:
+ return field.type_name();
+ case FieldDescriptorProto::TYPE_BYTES:
+ return "bytes";
+ case FieldDescriptorProto::TYPE_UINT32:
+ return "uint32";
+ case FieldDescriptorProto::TYPE_ENUM:
+ return field.type_name();
+ case FieldDescriptorProto::TYPE_SFIXED32:
+ return "sfixed32";
+ case FieldDescriptorProto::TYPE_SFIXED64:
+ return "sfixed64";
+ case FieldDescriptorProto::TYPE_SINT32:
+ return "sint32";
+ case FieldDescriptorProto::TYPE_SINT64:
+ return "sint64";
+ default:
+ // won't happen
+ return "void";
+ }
+}
+
+bool
+should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
+{
+ const int N = request.file_to_generate_size();
+ for (int i=0; i<N; i++) {
+ if (request.file_to_generate(i) == file) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // stream_proto
+} // android
diff --git a/tools/streaming_proto/stream_proto_utils.h b/tools/streaming_proto/stream_proto_utils.h
new file mode 100644
index 000000000000..5297eccba849
--- /dev/null
+++ b/tools/streaming_proto/stream_proto_utils.h
@@ -0,0 +1,29 @@
+#include <stdint.h>
+
+#include "google/protobuf/compiler/plugin.pb.h"
+#include "google/protobuf/io/zero_copy_stream_impl.h"
+
+namespace android {
+namespace stream_proto {
+
+using namespace google::protobuf;
+using namespace google::protobuf::compiler;
+using namespace std;
+
+/**
+ * Get encoded field id from a field.
+ */
+uint64_t get_field_id(const FieldDescriptorProto& field);
+
+/**
+ * Get the string name for a field.
+ */
+string get_proto_type(const FieldDescriptorProto& field);
+
+/**
+ * See if this is the file for this request, and not one of the imported ones.
+ */
+bool should_generate_for_file(const CodeGeneratorRequest& request, const string& file);
+
+} // stream_proto
+} // android
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index 03284d16e1be..d6f195f67188 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -6,6 +6,9 @@ namespace stream_proto {
using namespace std;
+// Indent
+const string INDENT = " ";
+
/**
* Capitalizes the string, removes underscores and makes the next letter
* capitalized, and makes the letter following numbers capitalized.
diff --git a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
index ad92e040b4be..e1ad78320611 100644
--- a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
+++ b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
@@ -24,6 +24,7 @@ import android.net.wifi.rtt.RangingRequest;
*/
interface IWifiRttManager
{
+ boolean isAvailable();
void startRanging(in IBinder binder, in String callingPackage, in RangingRequest request,
in IRttCallback callback);
}
diff --git a/wifi/java/android/net/wifi/rtt/RangingResultCallback.java b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
index 7405e82e2f5c..c8aea3c4aa62 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
@@ -36,7 +36,7 @@ import java.util.List;
*/
public abstract class RangingResultCallback {
/** @hide */
- @IntDef({STATUS_CODE_FAIL})
+ @IntDef({STATUS_CODE_FAIL, STATUS_CODE_FAIL_RTT_NOT_AVAILABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface RangingOperationStatus {
}
@@ -47,6 +47,14 @@ public abstract class RangingResultCallback {
public static final int STATUS_CODE_FAIL = 1;
/**
+ * A failure code for the whole ranging request operation. Indicates that the request failed due
+ * to RTT not being available - e.g. Wi-Fi was disabled. Use the
+ * {@link WifiRttManager#isAvailable()} and {@link WifiRttManager#ACTION_WIFI_RTT_STATE_CHANGED}
+ * to track RTT availability.
+ */
+ public static final int STATUS_CODE_FAIL_RTT_NOT_AVAILABLE = 2;
+
+ /**
* Called when a ranging operation failed in whole - i.e. no ranging operation to any of the
* devices specified in the request was attempted.
*
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index 435bb377043a..c7c09239ce0d 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -6,6 +6,7 @@ import static android.Manifest.permission.CHANGE_WIFI_STATE;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
import android.annotation.SystemService;
import android.content.Context;
import android.os.Binder;
@@ -22,11 +23,18 @@ import java.util.List;
* <p>
* The devices which can be ranged include:
* <li>Access Points (APs)
+ * <li>Wi-Fi Aware peers
* <p>
* Ranging requests are triggered using
* {@link #startRanging(RangingRequest, RangingResultCallback, Handler)}. Results (in case of
* successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)}
* callback.
+ * <p>
+ * Wi-Fi RTT may not be usable at some points, e.g. when Wi-Fi is disabled. To validate that
+ * the functionality is available use the {@link #isAvailable()} function. To track
+ * changes in RTT usability register for the {@link #ACTION_WIFI_RTT_STATE_CHANGED}
+ * broadcast. Note that this broadcast is not sticky - you should register for it and then
+ * check the above API to avoid a race condition.
*
* @hide RTT_API
*/
@@ -38,6 +46,18 @@ public class WifiRttManager {
private final Context mContext;
private final IWifiRttManager mService;
+ /**
+ * Broadcast intent action to indicate that the state of Wi-Fi RTT availability has changed.
+ * Use the {@link #isAvailable()} to query the current status.
+ * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
+ * the broadcast to check the current state of Wi-Fi RTT.
+ * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
+ * components will be launched.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_WIFI_RTT_STATE_CHANGED =
+ "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED";
+
/** @hide */
public WifiRttManager(Context context, IWifiRttManager service) {
mContext = context;
@@ -45,6 +65,22 @@ public class WifiRttManager {
}
/**
+ * Returns the current status of RTT API: whether or not RTT is available. To track
+ * changes in the state of RTT API register for the
+ * {@link #ACTION_WIFI_RTT_STATE_CHANGED} broadcast.
+ *
+ * @return A boolean indicating whether the app can use the RTT API at this time (true) or
+ * not (false).
+ */
+ public boolean isAvailable() {
+ try {
+ return mService.isAvailable();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Initiate a request to range to a set of devices specified in the {@link RangingRequest}.
* Results will be returned in the {@link RangingResultCallback} set of callbacks.
*