summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp28
-rw-r--r--Android.mk5
-rw-r--r--apct-tests/perftests/core/src/android/os/ParcelPerfTest.java80
-rw-r--r--api/current.txt45
-rw-r--r--api/system-current.txt47
-rw-r--r--api/test-current.txt45
-rw-r--r--cmds/incident_helper/src/main.cpp3
-rw-r--r--cmds/incident_helper/src/parsers/CpuFreqParser.cpp90
-rw-r--r--cmds/incident_helper/src/parsers/CpuFreqParser.h35
-rw-r--r--cmds/incident_helper/testdata/cpufreq.txt6
-rw-r--r--cmds/incident_helper/tests/CpuFreqParser_test.cpp130
-rw-r--r--cmds/incident_helper/tests/CpuInfoParser_test.cpp2
-rw-r--r--cmds/incidentd/src/FdBuffer.cpp15
-rw-r--r--cmds/incidentd/src/FdBuffer.h4
-rw-r--r--cmds/incidentd/src/Section.cpp3
-rw-r--r--cmds/incidentd/src/Section.h1
-rw-r--r--cmds/statsd/Android.mk4
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp100
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h33
-rw-r--r--cmds/statsd/src/StatsService.cpp168
-rw-r--r--cmds/statsd/src/StatsService.h23
-rw-r--r--cmds/statsd/src/anomaly/AnomalyMonitor.cpp5
-rw-r--r--cmds/statsd/src/anomaly/AnomalyMonitor.h9
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp276
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.h156
-rw-r--r--cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp153
-rw-r--r--cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h91
-rw-r--r--cmds/statsd/src/anomaly/indexed_priority_queue.h9
-rw-r--r--cmds/statsd/src/atoms.proto33
-rw-r--r--cmds/statsd/src/atoms_copy.proto3
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp19
-rw-r--r--cmds/statsd/src/condition/condition_util.cpp3
-rw-r--r--cmds/statsd/src/config/ConfigKey.h2
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp143
-rw-r--r--cmds/statsd/src/config/ConfigManager.h26
-rw-r--r--cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp7
-rw-r--r--cmds/statsd/src/external/CpuTimePerUidPuller.cpp7
-rw-r--r--cmds/statsd/src/external/ResourcePowerManagerPuller.cpp31
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp88
-rw-r--r--cmds/statsd/src/logd/LogEvent.h37
-rw-r--r--cmds/statsd/src/matchers/matcher_util.cpp19
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp40
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h12
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp55
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h19
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp4
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h4
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp84
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h16
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h15
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp17
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h11
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp25
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h6
-rw-r--r--cmds/statsd/src/metrics/duration_helper/DurationTracker.h99
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp75
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h18
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp187
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h22
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp42
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h1
-rw-r--r--cmds/statsd/src/stats_util.h5
-rw-r--r--cmds/statsd/src/statsd_config.proto58
-rw-r--r--cmds/statsd/tests/AnomalyMonitor_test.cpp2
-rw-r--r--cmds/statsd/tests/ConfigManager_test.cpp2
-rw-r--r--cmds/statsd/tests/LogEntryMatcher_test.cpp82
-rw-r--r--cmds/statsd/tests/MetricsManager_test.cpp89
-rw-r--r--cmds/statsd/tests/UidMap_test.cpp17
-rw-r--r--cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp388
-rw-r--r--cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp19
-rw-r--r--cmds/statsd/tests/indexed_priority_queue_test.cpp50
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp112
-rw-r--r--cmds/statsd/tests/metrics/EventMetricProducer_test.cpp6
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp201
-rw-r--r--cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp163
-rw-r--r--cmds/statsd/tests/metrics/OringDurationTracker_test.cpp236
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp42
-rw-r--r--cmds/webview_zygote/Android.mk2
-rw-r--r--core/java/android/app/Activity.java2
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/ApplicationLoaders.java10
-rw-r--r--core/java/android/app/ApplicationPackageManager.java20
-rw-r--r--core/java/android/app/Notification.java25
-rw-r--r--core/java/android/app/assist/AssistStructure.java29
-rw-r--r--core/java/android/app/slice/Slice.java56
-rw-r--r--core/java/android/app/slice/SliceProvider.java40
-rw-r--r--core/java/android/app/slice/SliceSpec.java117
-rw-r--r--core/java/android/app/slice/widget/ActionRow.java201
-rw-r--r--core/java/android/app/slice/widget/GridView.java192
-rw-r--r--core/java/android/app/slice/widget/LargeSliceAdapter.java224
-rw-r--r--core/java/android/app/slice/widget/LargeTemplateView.java131
-rw-r--r--core/java/android/app/slice/widget/MessageView.java77
-rw-r--r--core/java/android/app/slice/widget/RemoteInputView.java445
-rw-r--r--core/java/android/app/slice/widget/ShortcutView.java175
-rw-r--r--core/java/android/app/slice/widget/SliceView.java422
-rw-r--r--core/java/android/app/slice/widget/SliceViewUtil.java198
-rw-r--r--core/java/android/app/slice/widget/SmallTemplateView.java211
-rw-r--r--core/java/android/app/usage/UsageEvents.java6
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java36
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java103
-rw-r--r--core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java20
-rw-r--r--core/java/android/content/pm/IPackageInstallObserver.aidl27
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl5
-rw-r--r--core/java/android/content/pm/PackageInstaller.java3
-rw-r--r--core/java/android/content/pm/PackageManager.java269
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java34
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java112
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java77
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java102
-rw-r--r--core/java/android/hardware/location/ContextHubInfo.java247
-rw-r--r--core/java/android/hardware/radio/ITuner.aidl12
-rw-r--r--core/java/android/hardware/radio/ITunerCallback.aidl5
-rw-r--r--core/java/android/hardware/radio/RadioTuner.java68
-rw-r--r--core/java/android/hardware/radio/TunerAdapter.java19
-rw-r--r--core/java/android/hardware/radio/TunerCallbackAdapter.java7
-rw-r--r--core/java/android/net/metrics/WakeupEvent.java4
-rw-r--r--core/java/android/net/metrics/WakeupStats.java3
-rw-r--r--core/java/android/os/Build.java2
-rw-r--r--core/java/android/os/GraphicsEnvironment.java106
-rw-r--r--core/java/android/os/IStatsManager.aidl2
-rw-r--r--core/java/android/os/IUserManager.aidl4
-rw-r--r--core/java/android/os/Parcel.java85
-rw-r--r--core/java/android/os/PowerManager.java12
-rw-r--r--core/java/android/os/Process.java3
-rw-r--r--core/java/android/os/RemoteException.java6
-rw-r--r--core/java/android/provider/SearchIndexableData.java2
-rw-r--r--core/java/android/provider/Settings.java21
-rw-r--r--core/java/android/service/autofill/InternalSanitizer.java5
-rw-r--r--core/java/android/service/autofill/SaveInfo.java5
-rw-r--r--core/java/android/service/autofill/TextValueSanitizer.java14
-rw-r--r--core/java/android/text/format/Formatter.java6
-rw-r--r--core/java/android/util/AndroidException.java6
-rw-r--r--core/java/android/util/FeatureFlagUtils.java22
-rw-r--r--core/java/android/util/MutableInt.java (renamed from core/java/android/os/IStatsCallbacks.aidl)20
-rw-r--r--core/java/android/util/MutableLong.java27
-rw-r--r--core/java/android/util/StatsManager.java13
-rw-r--r--core/java/android/util/proto/ProtoUtils.java12
-rw-r--r--core/java/android/view/Gravity.java33
-rw-r--r--core/java/android/view/RenderNodeAnimator.java10
-rw-r--r--core/java/android/view/SurfaceControl.java26
-rw-r--r--core/java/android/view/View.java199
-rw-r--r--core/java/android/view/ViewGroup.java15
-rw-r--r--core/java/android/view/ViewRootImpl.java112
-rw-r--r--core/java/android/view/WindowManagerPolicy.java8
-rw-r--r--core/java/android/webkit/UserPackage.java4
-rw-r--r--core/java/android/webkit/WebSettings.java30
-rw-r--r--core/java/android/widget/Editor.java50
-rw-r--r--core/java/com/android/internal/policy/DecorView.java6
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java8
-rw-r--r--core/java/com/android/internal/widget/Magnifier.java150
-rw-r--r--core/java/com/android/internal/widget/RecyclerView.java2
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android/graphics/FontFamily.cpp6
-rw-r--r--core/jni/android/graphics/Paint.cpp7
-rw-r--r--core/jni/android/graphics/Typeface.cpp2
-rw-r--r--core/jni/android_app_ApplicationLoaders.cpp54
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp16
-rw-r--r--core/jni/android_text_StaticLayout.cpp172
-rw-r--r--core/jni/android_view_SurfaceControl.cpp10
-rw-r--r--core/proto/android/app/notification.proto45
-rw-r--r--core/proto/android/os/cpufreq.proto41
-rw-r--r--core/proto/android/os/incident.proto11
-rw-r--r--core/proto/android/providers/settings.proto5
-rw-r--r--core/proto/android/server/activitymanagerservice.proto161
-rw-r--r--core/proto/android/util/common.proto10
-rw-r--r--core/res/Android.mk3
-rw-r--r--core/res/res/values-ar/strings.xml8
-rw-r--r--core/res/res/values-fr-rCA/strings.xml2
-rw-r--r--core/res/res/values-sw/strings.xml56
-rw-r--r--core/res/res/values/strings.xml8
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java3
-rw-r--r--core/tests/coretests/src/android/text/format/FormatterTest.java16
-rw-r--r--core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java16
-rw-r--r--data/fonts/Android.mk23
-rw-r--r--data/fonts/fonts.xml28
-rw-r--r--docs/html/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.pngbin0 -> 25603 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.pngbin0 -> 12983 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.pngbin0 -> 15443 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.pngbin0 -> 15839 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.pngbin0 -> 72941 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.pngbin0 -> 12787 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.pngbin0 -> 31494 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.pngbin0 -> 22690 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.pngbin0 -> 21822 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.pngbin0 -> 32384 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.pngbin0 -> 32315 bytes
-rw-r--r--graphics/java/android/graphics/drawable/RippleBackground.java137
-rw-r--r--graphics/java/android/graphics/drawable/RippleComponent.java244
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java130
-rw-r--r--graphics/java/android/graphics/drawable/RippleForeground.java344
-rw-r--r--legacy-test/Android.mk1
-rw-r--r--libs/hwui/Android.bp31
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp9
-rw-r--r--libs/hwui/hwui/Paint.h6
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp13
-rw-r--r--libs/hwui/hwui/Typeface.cpp2
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp168
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h6
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp6
-rw-r--r--location/Android.mk2
-rw-r--r--location/tests/locationtests/Android.mk10
-rw-r--r--location/tests/locationtests/AndroidManifest.xml12
-rw-r--r--location/tests/locationtests/AndroidTest.xml27
-rw-r--r--location/tests/locationtests/src/android/location/GnssStatusTest.java130
-rw-r--r--location/tests/locationtests/src/android/location/GpsStatusTest.java356
-rw-r--r--location/tests/locationtests/src/android/location/SatelliteInfo.java155
-rw-r--r--media/java/android/media/MediaDrm.java68
-rw-r--r--media/java/android/media/MediaMetadata.java66
-rw-r--r--packages/CarrierDefaultApp/res/values-sw/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml15
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml17
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml15
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml15
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml15
-rw-r--r--packages/SettingsLib/res/values/strings.xml15
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java9
-rw-r--r--packages/Shell/res/values-sw/strings.xml6
-rw-r--r--packages/SystemUI/res-keyguard/values-ar/strings.xml4
-rw-r--r--packages/SystemUI/res/values-ar/config.xml1
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml12
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml18
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml40
-rw-r--r--packages/SystemUI/shared/Android.mk2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java17
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java73
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ChoreographerCompat.java33
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java64
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/OverviewProxyService.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java1
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java29
-rw-r--r--services/autofill/java/com/android/server/autofill/ViewState.java11
-rw-r--r--services/core/Android.mk3
-rw-r--r--services/core/java/com/android/server/ForceAppStandbyTracker.java386
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java32
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java54
-rw-r--r--services/core/java/com/android/server/am/AppBindRecord.java17
-rw-r--r--services/core/java/com/android/server/am/ConnectionRecord.java69
-rw-r--r--services/core/java/com/android/server/am/IntentBindRecord.java32
-rw-r--r--services/core/java/com/android/server/am/ProviderMap.java37
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java143
-rw-r--r--services/core/java/com/android/server/am/UriPermissionOwner.java23
-rw-r--r--services/core/java/com/android/server/am/UserController.java38
-rw-r--r--services/core/java/com/android/server/broadcastradio/Tuner.java31
-rw-r--r--services/core/java/com/android/server/broadcastradio/TunerCallback.java8
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java8
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java9
-rw-r--r--services/core/java/com/android/server/job/controllers/BackgroundJobsController.java284
-rw-r--r--services/core/java/com/android/server/location/ContextHubService.java553
-rw-r--r--services/core/java/com/android/server/location/ContextHubServiceTransaction.java161
-rw-r--r--services/core/java/com/android/server/location/ContextHubServiceUtil.java168
-rw-r--r--services/core/java/com/android/server/location/ContextHubTransactionManager.java338
-rw-r--r--services/core/java/com/android/server/media/AudioPlayerStateMonitor.java158
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java48
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java29
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java20
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java16
-rw-r--r--services/core/java/com/android/server/pm/Installer.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java8
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java13
-rw-r--r--services/core/java/com/android/server/pm/dex/DexLogger.java116
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java23
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java13
-rw-r--r--services/core/java/com/android/server/power/BatterySaverPolicy.java69
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java2
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java74
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java21
-rw-r--r--services/core/java/com/android/server/wm/AppWindowAnimator.java16
-rw-r--r--services/core/java/com/android/server/wm/BlackFrame.java29
-rw-r--r--services/core/java/com/android/server/wm/CircularDisplayMask.java7
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/DimLayer.java380
-rw-r--r--services/core/java/com/android/server/wm/DimLayerController.java403
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java195
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java300
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java75
-rw-r--r--services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java6
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java9
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java27
-rw-r--r--services/core/java/com/android/server/wm/StrictModeFlash.java5
-rw-r--r--services/core/java/com/android/server/wm/SurfaceBuilderFactory.java25
-rw-r--r--services/core/java/com/android/server/wm/Task.java62
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java108
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java120
-rw-r--r--services/core/java/com/android/server/wm/Watermark.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java190
-rw-r--r--services/core/java/com/android/server/wm/WindowLayersController.java273
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java46
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java163
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java101
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java3
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/BroadcastRadio/Tuner.cpp61
-rw-r--r--services/core/jni/BroadcastRadio/TunerCallback.cpp8
-rw-r--r--services/core/jni/BroadcastRadio/convert.cpp47
-rw-r--r--services/core/jni/BroadcastRadio/convert.h2
-rw-r--r--services/core/jni/com_android_server_location_ContextHubService.cpp1201
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/java/com/android/server/SystemServer.java4
-rw-r--r--services/robotests/Android.mk2
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java24
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java80
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java155
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java65
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/DimmerTests.java207
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java186
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java327
-rw-r--r--services/usage/java/com/android/server/usage/AppIdleHistory.java38
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java65
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java2
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityGsm.java22
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java22
-rw-r--r--telephony/java/android/telephony/CellIdentityWcdma.java22
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java31
-rw-r--r--test-mock/Android.mk144
-rw-r--r--test-mock/api/android-test-mock-current.txt (renamed from test-runner/api/android-test-mock-current.txt)1
-rw-r--r--test-mock/api/android-test-mock-removed.txt (renamed from test-runner/api/android-test-mock-removed.txt)0
-rw-r--r--test-mock/api/apicheck_msg_android_test_mock.txt (renamed from test-runner/api/apicheck_msg_android_test_mock.txt)0
-rw-r--r--test-mock/src/android/test/mock/MockApplication.java (renamed from test-runner/src/android/test/mock/MockApplication.java)0
-rw-r--r--test-mock/src/android/test/mock/MockContentProvider.java (renamed from test-runner/src/android/test/mock/MockContentProvider.java)0
-rw-r--r--test-mock/src/android/test/mock/MockContentResolver.java (renamed from test-runner/src/android/test/mock/MockContentResolver.java)0
-rw-r--r--test-mock/src/android/test/mock/MockContext.java (renamed from test-runner/src/android/test/mock/MockContext.java)0
-rw-r--r--test-mock/src/android/test/mock/MockCursor.java (renamed from test-runner/src/android/test/mock/MockCursor.java)0
-rw-r--r--test-mock/src/android/test/mock/MockDialogInterface.java (renamed from test-runner/src/android/test/mock/MockDialogInterface.java)0
-rw-r--r--test-mock/src/android/test/mock/MockIContentProvider.java (renamed from test-runner/src/android/test/mock/MockIContentProvider.java)0
-rw-r--r--test-mock/src/android/test/mock/MockPackageManager.java (renamed from test-runner/src/android/test/mock/MockPackageManager.java)12
-rw-r--r--test-mock/src/android/test/mock/MockResources.java (renamed from test-runner/src/android/test/mock/MockResources.java)0
-rw-r--r--test-mock/src/android/test/mock/package.html (renamed from test-runner/src/android/test/mock/package.html)0
-rw-r--r--test-runner/Android.mk131
-rw-r--r--tools/aapt2/Debug.cpp1
-rw-r--r--tools/aapt2/Resource.h5
-rw-r--r--tools/aapt2/cmd/Link.cpp93
-rw-r--r--tools/aapt2/io/FileStream.cpp45
-rw-r--r--tools/aapt2/io/FileStream.h17
-rw-r--r--tools/aapt2/io/FileStream_test.cpp1
-rw-r--r--tools/aapt2/java/AnnotationProcessor.cpp12
-rw-r--r--tools/aapt2/java/AnnotationProcessor.h6
-rw-r--r--tools/aapt2/java/AnnotationProcessor_test.cpp28
-rw-r--r--tools/aapt2/java/ClassDefinition.cpp49
-rw-r--r--tools/aapt2/java/ClassDefinition.h62
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp83
-rw-r--r--tools/aapt2/java/JavaClassGenerator.h22
-rw-r--r--tools/aapt2/java/JavaClassGenerator_test.cpp77
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.h3
-rw-r--r--tools/aapt2/java/ManifestClassGenerator_test.cpp11
-rw-r--r--tools/aapt2/java/ProguardRules.cpp36
-rw-r--r--tools/aapt2/java/ProguardRules.h13
-rw-r--r--tools/aapt2/java/ProguardRules_test.cpp49
-rw-r--r--tools/aapt2/text/Printer.cpp19
-rw-r--r--tools/aapt2/text/Printer.h6
-rw-r--r--tools/apilint/apilint.py42
-rwxr-xr-xtools/fonts/fontchain_linter.py (renamed from tools/fonts/fontchain_lint.py)128
-rw-r--r--tools/stats_log_api_gen/Android.bp8
-rw-r--r--tools/stats_log_api_gen/main.cpp18
-rw-r--r--vr/Android.mk1
-rw-r--r--wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java26
-rw-r--r--wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl2
-rw-r--r--wifi/java/android/net/wifi/aware/PublishConfig.java56
-rw-r--r--wifi/java/android/net/wifi/aware/SubscribeConfig.java156
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareManager.java47
-rw-r--r--wifi/java/android/net/wifi/rtt/WifiRttManager.java2
-rw-r--r--wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java51
390 files changed, 11930 insertions, 10019 deletions
diff --git a/Android.bp b/Android.bp
index 24a9bca1844c..2ea489442b89 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@ cc_library {
// runtime, as well as the only protos that are actually
// needed by the device.
srcs: [
+ "core/proto/android/os/cpufreq.proto",
"core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
@@ -83,6 +84,7 @@ gensrcs {
],
srcs: [
+ "core/proto/android/os/cpufreq.proto",
"core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
@@ -132,3 +134,29 @@ java_library {
dxflags: ["--core-library"],
installable: false,
}
+
+python_defaults {
+ name: "base_default",
+ version: {
+ py2: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ py3: {
+ enabled: false,
+ embedded_launcher: false,
+ },
+ },
+}
+
+python_binary_host {
+ name: "fontchain_linter",
+ defaults: ["base_default"],
+ main: "tools/fonts/fontchain_linter.py",
+ srcs: [
+ "tools/fonts/fontchain_linter.py",
+ ],
+ libs: [
+ "fontTools",
+ ],
+}
diff --git a/Android.mk b/Android.mk
index 7d0559acbe45..08a8b645fdb6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -169,7 +169,6 @@ LOCAL_SRC_FILES += \
core/java/android/content/pm/IPackageDataObserver.aidl \
core/java/android/content/pm/IPackageDeleteObserver.aidl \
core/java/android/content/pm/IPackageDeleteObserver2.aidl \
- core/java/android/content/pm/IPackageInstallObserver.aidl \
core/java/android/content/pm/IPackageInstallObserver2.aidl \
core/java/android/content/pm/IPackageInstaller.aidl \
core/java/android/content/pm/IPackageInstallerCallback.aidl \
@@ -274,7 +273,6 @@ LOCAL_SRC_FILES += \
core/java/android/os/IRecoverySystemProgressListener.aidl \
core/java/android/os/IRemoteCallback.aidl \
core/java/android/os/ISchedulingPolicyService.aidl \
- core/java/android/os/IStatsCallbacks.aidl \
core/java/android/os/IStatsCompanionService.aidl \
core/java/android/os/IStatsManager.aidl \
core/java/android/os/IThermalEventListener.aidl \
@@ -638,6 +636,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
framework-protos \
android.hidl.base-V1.0-java \
android.hardware.cas-V1.0-java \
+ android.hardware.contexthub-V1.0-java \
android.hardware.health-V1.0-java-constants \
android.hardware.thermal-V1.0-java-constants \
android.hardware.tv.input-V1.0-java-constants \
@@ -931,7 +930,7 @@ non_base_dirs := \
../opt/net/voip/src/java/android/net/sip
framework_base_android_test_mock_src_files := \
- $(call all-java-files-under, test-runner/src/android/test/mock)
+ $(call all-java-files-under, test-mock/src/android/test/mock)
framework_base_android_test_runner_excluding_mock_src_files := \
$(filter-out $(framework_base_android_test_mock_src_files), $(call all-java-files-under, test-runner/src))
diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
index a92597f131c3..6e4c9c54a2f3 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
@@ -27,6 +27,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ParcelPerfTest {
@@ -167,4 +171,80 @@ public class ParcelPerfTest {
Parcel.obtain().recycle();
}
}
+
+ @Test
+ public void timeWriteException() {
+ timeWriteException(false);
+ }
+
+ @Test
+ public void timeWriteExceptionWithStackTraceParceling() {
+ timeWriteException(true);
+ }
+
+ @Test
+ public void timeReadException() {
+ timeReadException(false);
+ }
+
+ @Test
+ public void timeReadExceptionWithStackTraceParceling() {
+ timeReadException(true);
+ }
+
+ private void timeWriteException(boolean enableParceling) {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(true);
+ }
+ try {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ Parcel p = Parcel.obtain();
+ SecurityException e = new SecurityException("TestMessage");
+ while (state.keepRunning()) {
+ p.setDataPosition(0);
+ p.writeException(e);
+ }
+ } finally {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(false);
+ }
+ }
+ }
+
+ private void timeReadException(boolean enableParceling) {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(true);
+ }
+ try {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ Parcel p = Parcel.obtain();
+ String msg = "TestMessage";
+ p.writeException(new SecurityException(msg));
+ p.setDataPosition(0);
+ // First verify that remote cause is set (if parceling is enabled)
+ try {
+ p.readException();
+ } catch (SecurityException e) {
+ assertEquals(e.getMessage(), msg);
+ if (enableParceling) {
+ assertTrue(e.getCause() instanceof RemoteException);
+ } else {
+ assertNull(e.getCause());
+ }
+ }
+
+ while (state.keepRunning()) {
+ p.setDataPosition(0);
+ try {
+ p.readException();
+ } catch (SecurityException expected) {
+ }
+ }
+ } finally {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(false);
+ }
+ }
+ }
+
}
diff --git a/api/current.txt b/api/current.txt
index 6043aba0a71e..7e4ee1a51406 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6973,11 +6973,12 @@ package android.app.slice {
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+ method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
+ method public android.app.slice.SliceSpec getSpec();
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7012,6 +7013,7 @@ package android.app.slice {
method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
method public android.app.slice.Slice build();
+ method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
}
public final class SliceItem implements android.os.Parcelable {
@@ -7042,7 +7044,8 @@ package android.app.slice {
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7051,25 +7054,14 @@ package android.app.slice {
field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
}
-}
-
-package android.app.slice.widget {
-
- public class SliceView extends android.view.ViewGroup {
- ctor public SliceView(android.content.Context);
- ctor public SliceView(android.content.Context, android.util.AttributeSet);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
- method public void clearSlice();
- method public java.lang.String getMode();
- method protected void onLayout(boolean, int, int, int, int);
- method public void setMode(java.lang.String);
- method public void setScrollable(boolean);
- method public boolean setSlice(android.net.Uri);
- method public void showSlice(android.app.slice.Slice);
- field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
- field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
- field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
+ public final class SliceSpec implements android.os.Parcelable {
+ ctor public SliceSpec(java.lang.String, int);
+ method public boolean canRender(android.app.slice.SliceSpec);
+ method public int describeContents();
+ method public int getRevision();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
}
}
@@ -38624,7 +38616,6 @@ package android.system {
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -38650,7 +38641,6 @@ package android.system {
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -46087,6 +46077,7 @@ package android.view {
method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+ method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -46421,6 +46412,7 @@ package android.view {
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyFallback(android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyPreIme(int, android.view.KeyEvent);
@@ -46474,6 +46466,7 @@ package android.view {
method public void refreshDrawableState();
method public void releasePointerCapture();
method public boolean removeCallbacks(java.lang.Runnable);
+ method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
method public void requestApplyInsets();
@@ -46892,6 +46885,10 @@ package android.view {
method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
}
+ public static abstract interface View.OnKeyFallbackListener {
+ method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent);
+ }
+
public static abstract interface View.OnKeyListener {
method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index f0124ae05fff..a6e1eabbd638 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7417,11 +7417,12 @@ package android.app.slice {
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+ method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
+ method public android.app.slice.SliceSpec getSpec();
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7456,6 +7457,7 @@ package android.app.slice {
method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
method public android.app.slice.Slice build();
+ method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
}
public final class SliceItem implements android.os.Parcelable {
@@ -7486,7 +7488,8 @@ package android.app.slice {
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7495,25 +7498,14 @@ package android.app.slice {
field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
}
-}
-
-package android.app.slice.widget {
-
- public class SliceView extends android.view.ViewGroup {
- ctor public SliceView(android.content.Context);
- ctor public SliceView(android.content.Context, android.util.AttributeSet);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
- method public void clearSlice();
- method public java.lang.String getMode();
- method protected void onLayout(boolean, int, int, int, int);
- method public void setMode(java.lang.String);
- method public void setScrollable(boolean);
- method public boolean setSlice(android.net.Uri);
- method public void showSlice(android.app.slice.Slice);
- field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
- field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
- field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
+ public final class SliceSpec implements android.os.Parcelable {
+ ctor public SliceSpec(java.lang.String, int);
+ method public boolean canRender(android.app.slice.SliceSpec);
+ method public int describeContents();
+ method public int getRevision();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
}
}
@@ -41928,7 +41920,6 @@ package android.system {
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -41954,7 +41945,6 @@ package android.system {
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -48196,8 +48186,8 @@ package android.util {
}
public final class StatsManager {
- method public byte[] getData(java.lang.String);
method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
+ method public byte[] getData(java.lang.String);
method public boolean removeConfiguration(java.lang.String);
}
@@ -49832,6 +49822,7 @@ package android.view {
method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+ method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -50166,6 +50157,7 @@ package android.view {
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyFallback(android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyPreIme(int, android.view.KeyEvent);
@@ -50219,6 +50211,7 @@ package android.view {
method public void refreshDrawableState();
method public void releasePointerCapture();
method public boolean removeCallbacks(java.lang.Runnable);
+ method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
method public void requestApplyInsets();
@@ -50637,6 +50630,10 @@ package android.view {
method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
}
+ public static abstract interface View.OnKeyFallbackListener {
+ method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent);
+ }
+
public static abstract interface View.OnKeyListener {
method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 390e7c948e60..f9dd65c6ad14 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7047,11 +7047,12 @@ package android.app.slice {
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+ method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
+ method public android.app.slice.SliceSpec getSpec();
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7086,6 +7087,7 @@ package android.app.slice {
method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
method public android.app.slice.Slice build();
+ method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
}
public final class SliceItem implements android.os.Parcelable {
@@ -7116,7 +7118,8 @@ package android.app.slice {
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7125,25 +7128,14 @@ package android.app.slice {
field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
}
-}
-
-package android.app.slice.widget {
-
- public class SliceView extends android.view.ViewGroup {
- ctor public SliceView(android.content.Context);
- ctor public SliceView(android.content.Context, android.util.AttributeSet);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
- method public void clearSlice();
- method public java.lang.String getMode();
- method protected void onLayout(boolean, int, int, int, int);
- method public void setMode(java.lang.String);
- method public void setScrollable(boolean);
- method public boolean setSlice(android.net.Uri);
- method public void showSlice(android.app.slice.Slice);
- field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
- field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
- field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
+ public final class SliceSpec implements android.os.Parcelable {
+ ctor public SliceSpec(java.lang.String, int);
+ method public boolean canRender(android.app.slice.SliceSpec);
+ method public int describeContents();
+ method public int getRevision();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
}
}
@@ -39015,7 +39007,6 @@ package android.system {
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -39041,7 +39032,6 @@ package android.system {
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -46744,6 +46734,7 @@ package android.view {
method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+ method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -47080,6 +47071,7 @@ package android.view {
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyFallback(android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyPreIme(int, android.view.KeyEvent);
@@ -47133,6 +47125,7 @@ package android.view {
method public void refreshDrawableState();
method public void releasePointerCapture();
method public boolean removeCallbacks(java.lang.Runnable);
+ method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
method public void requestApplyInsets();
@@ -47555,6 +47548,10 @@ package android.view {
method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
}
+ public static abstract interface View.OnKeyFallbackListener {
+ method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent);
+ }
+
public static abstract interface View.OnKeyListener {
method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
}
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 8239d8ec92ed..c8a0883d493c 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "incident_helper"
+#include "parsers/CpuFreqParser.h"
#include "parsers/CpuInfoParser.h"
#include "parsers/KernelWakesParser.h"
#include "parsers/PageTypeInfoParser.h"
@@ -60,6 +61,8 @@ static TextParserBase* selectParser(int section) {
return new KernelWakesParser();
case 2003:
return new CpuInfoParser();
+ case 2004:
+ return new CpuFreqParser();
default:
return NULL;
}
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
new file mode 100644
index 000000000000..02f1ce7cc0fc
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <unistd.h>
+
+#include "frameworks/base/core/proto/android/os/cpufreq.proto.h"
+#include "ih_util.h"
+#include "CpuFreqParser.h"
+
+using namespace android::os;
+
+status_t
+CpuFreqParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+
+ // parse header
+ reader.readLine(&line);
+ header_t header = parseHeader(line, TAB_DELIMITER);
+ if (header.size() < 1) {
+ fprintf(stderr, "Bad header: %s\n", line.c_str());
+ return BAD_VALUE;
+ }
+ const int numCpus = (int)header.size() - 1;
+ vector<pair<int, long long>> cpucores[numCpus];
+
+ // parse freq and time
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ record_t record = parseRecord(line, TAB_DELIMITER);
+ if (record.size() != header.size()) {
+ fprintf(stderr, "Bad line: %s\n", line.c_str());
+ continue;
+ }
+
+ int freq = toInt(record[0]);
+ for (int i=0; i<numCpus; i++) {
+ if (strcmp(record[i+1].c_str(), "N/A") == 0) {
+ continue;
+ }
+ cpucores[i].push_back(make_pair(freq, toLongLong(record[i+1])));
+ }
+ }
+
+ ProtoOutputStream proto;
+
+ long jiffyHz = sysconf(_SC_CLK_TCK);
+ proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
+
+ for (int i=0; i<numCpus; i++) {
+ long long token = proto.start(CpuFreq::CPU_FREQS);
+ proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
+ for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
+ long long stateToken = proto.start(CpuFreqStats::TIMES);
+ proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
+ proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
+ proto.end(stateToken);
+ }
+ 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/CpuFreqParser.h b/cmds/incident_helper/src/parsers/CpuFreqParser.h
new file mode 100644
index 000000000000..470d56834781
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.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 CPU_FREQ_PARSER_H
+#define CPU_FREQ_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu frequency parser, parses text in /sys/devices/system/cpu/cpufreq/all_time_in_state
+ */
+class CpuFreqParser : public TextParserBase {
+public:
+ CpuFreqParser() : TextParserBase(String8("CpuFreqParser")) {};
+ ~CpuFreqParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // CPU_FREQ_PARSER_H
diff --git a/cmds/incident_helper/testdata/cpufreq.txt b/cmds/incident_helper/testdata/cpufreq.txt
new file mode 100644
index 000000000000..6472839e455e
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpufreq.txt
@@ -0,0 +1,6 @@
+freq cpu0 cpu1 cpu2 cpu3
+307200 23860761 23860761 23890935 23890935
+384000 83124 83124 29383 29383
+748800 N/A N/A 10547 10547
+768000 22652 22652 N/A N/A
+825600 N/A N/A 13173 13173
diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
new file mode 100644
index 000000000000..1c2f9e59308d
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "CpuFreqParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpufreq.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 CpuFreqParserTest : 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(CpuFreqParserTest, Success) {
+ const string testFile = kTestDataPath + "cpufreq.txt";
+ CpuFreqParser parser;
+ CpuFreq expected;
+
+ long jiffyHz = sysconf(_SC_CLK_TCK);
+ expected.set_jiffy_hz(jiffyHz);
+
+ CpuFreqStats::TimeInState* state;
+
+ CpuFreqStats* cpu0 = expected.add_cpu_freqs();
+ cpu0->set_cpu_name("cpu0");
+ state = cpu0->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23860761);
+ state = cpu0->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(83124);
+ state = cpu0->add_times();
+ state->set_state_khz(768000);
+ state->set_time_jiffy(22652);
+
+ CpuFreqStats* cpu1 = expected.add_cpu_freqs();
+ cpu1->set_cpu_name("cpu1");
+ state = cpu1->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23860761);
+ state = cpu1->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(83124);
+ state = cpu1->add_times();
+ state->set_state_khz(768000);
+ state->set_time_jiffy(22652);
+
+ CpuFreqStats* cpu2 = expected.add_cpu_freqs();
+ cpu2->set_cpu_name("cpu2");
+ state = cpu2->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23890935);
+ state = cpu2->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(29383);
+ state = cpu2->add_times();
+ state->set_state_khz(748800);
+ state->set_time_jiffy(10547);
+ state = cpu2->add_times();
+ state->set_state_khz(825600);
+ state->set_time_jiffy(13173);
+
+ CpuFreqStats* cpu3 = expected.add_cpu_freqs();
+ cpu3->set_cpu_name("cpu3");
+ state = cpu3->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23890935);
+ state = cpu3->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(29383);
+ state = cpu3->add_times();
+ state->set_state_khz(748800);
+ state->set_time_jiffy(10547);
+ state = cpu3->add_times();
+ state->set_state_khz(825600);
+ state->set_time_jiffy(13173);
+
+ 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/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
index 57ad15cf3910..bbc14bca3efc 100644
--- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -56,7 +56,7 @@ protected:
const string kTestDataPath = kTestPath + "/testdata/";
};
-TEST_F(CpuInfoParserTest, HasSwapInfo) {
+TEST_F(CpuInfoParserTest, Success) {
const string testFile = kTestDataPath + "cpuinfo.txt";
CpuInfoParser parser;
CpuInfo expected;
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index b7633a435653..30dd339a629b 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <wait.h>
+const bool DEBUG = false;
const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
@@ -71,9 +72,11 @@ FdBuffer::read(int fd, int64_t timeout)
mTimedOut = true;
break;
} else if (count < 0) {
+ if (DEBUG) ALOGD("poll failed: %s", strerror(errno));
return -errno;
} else {
if ((pfds.revents & POLLERR) != 0) {
+ if (DEBUG) ALOGD("return event has error %s", strerror(errno));
return errno != 0 ? -errno : UNKNOWN_ERROR;
} else {
ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
@@ -81,6 +84,7 @@ FdBuffer::read(int fd, int64_t timeout)
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
} else {
+ if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno));
return -errno;
}
} else if (amt == 0) {
@@ -95,7 +99,7 @@ FdBuffer::read(int fd, int64_t timeout)
}
status_t
-FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs)
+FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs)
{
struct pollfd pfds[] = {
{ .fd = fd, .events = POLLIN },
@@ -135,12 +139,18 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
mTimedOut = true;
break;
} else if (count < 0) {
+ if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno));
return -errno;
}
// make sure no errors occur on any fds
for (int i = 0; i < 3; ++i) {
if ((pfds[i].revents & POLLERR) != 0) {
+ if (i == 0 && isSysfs) {
+ if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd);
+ continue;
+ }
+ if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
return errno != 0 ? -errno : UNKNOWN_ERROR;
}
}
@@ -155,6 +165,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
}
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+ if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno));
return -errno;
} // otherwise just continue
} else if (amt == 0) { // reach EOF so don't have to poll pfds[0].
@@ -176,6 +187,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
}
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+ if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno));
return -errno;
} // otherwise just continue
} else {
@@ -202,6 +214,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou
ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+ if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno));
return -errno;
} // otherwise just continue
} else if (amt == 0) {
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 8857ae714dac..48dc855e71b2 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -47,8 +47,10 @@ public:
* and stores the processed data from 'fromFd' in memory for later usage.
* This function behaves in a streaming fashion in order to save memory usage.
* Returns NO_ERROR if there were no errors or if we timed out.
+ *
+ * Poll will return POLLERR if fd is from sysfs, handle this edge case.
*/
- status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs);
+ status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false);
/**
* Whether we timed out.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 892bcca17b86..c08b9ea80c04 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -232,6 +232,7 @@ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
mFilename(filename)
{
name = filename;
+ mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
}
FileSection::~FileSection() {}
@@ -264,7 +265,7 @@ FileSection::Execute(ReportRequestSet* requests) const
// parent process
status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
- this->timeoutMs);
+ this->timeoutMs, mIsSysfs);
if (readStatus != NO_ERROR || buffer.timedOut()) {
ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 0a1e03eb6f41..64558a6b732b 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -69,6 +69,7 @@ public:
private:
const char* mFilename;
+ bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately
};
/**
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d8603636c7c0..1b2f9da22ec1 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -15,13 +15,13 @@
LOCAL_PATH:= $(call my-dir)
statsd_common_src := \
- ../../core/java/android/os/IStatsCallbacks.aidl \
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
src/stats_log.proto \
src/statsd_config.proto \
src/atoms_copy.proto \
src/anomaly/AnomalyMonitor.cpp \
+ src/anomaly/AnomalyTracker.cpp \
src/condition/CombinationConditionTracker.cpp \
src/condition/condition_util.cpp \
src/condition/SimpleConditionTracker.cpp \
@@ -40,7 +40,6 @@ statsd_common_src := \
src/matchers/CombinationLogMatchingTracker.cpp \
src/matchers/matcher_util.cpp \
src/matchers/SimpleLogMatchingTracker.cpp \
- src/anomaly/DiscreteAnomalyTracker.cpp \
src/metrics/MetricProducer.cpp \
src/metrics/EventMetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
@@ -179,4 +178,3 @@ statsd_common_aidl_includes:=
statsd_common_c_includes:=
include $(BUILD_NATIVE_TEST)
-
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index b764ce56767b..fcb5107c31fc 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -50,13 +50,24 @@ const int FIELD_ID_UID = 1;
const int FIELD_ID_NAME = 2;
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
- const std::function<void(const vector<uint8_t>&)>& pushLog)
- : mUidMap(uidMap), mPushLog(pushLog) {
+ const sp<AnomalyMonitor>& anomalyMonitor,
+ const std::function<void(const ConfigKey&)>& sendBroadcast)
+ : mUidMap(uidMap), mAnomalyMonitor(anomalyMonitor), mSendBroadcast(sendBroadcast) {
}
StatsLogProcessor::~StatsLogProcessor() {
}
+void StatsLogProcessor::onAnomalyAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
+ for (const auto& anomaly : anomalySet) {
+ for (const auto& itr : mMetricsManagers) {
+ itr.second->onAnomalyAlarmFired(timestampNs, anomaly);
+ }
+ }
+}
+
// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
// pass the event to metrics managers.
@@ -93,6 +104,7 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig
unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
if (newMetricsManager->isConfigValid()) {
mUidMap->OnConfigUpdated(key);
+ newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
mMetricsManagers[key] = std::move(newMetricsManager);
// Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
ALOGD("StatsdConfig valid");
@@ -102,12 +114,27 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig
}
}
-vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
+size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) {
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end()) {
ALOGW("Config source %s does not exist", key.ToString().c_str());
- return vector<uint8_t>();
+ return 0;
}
+ return it->second->byteSize();
+}
+
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
+ auto it = mMetricsManagers.find(key);
+ if (it == mMetricsManagers.end()) {
+ ALOGW("Config source %s does not exist", key.ToString().c_str());
+ return;
+ }
+
+ // This allows another broadcast to be sent within the rate-limit period if we get close to
+ // filling the buffer again soon.
+ mBroadcastTimesMutex.lock();
+ mLastBroadcastTimes.erase(key);
+ mBroadcastTimesMutex.unlock();
ProtoOutputStream proto;
@@ -131,17 +158,18 @@ vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
- vector<uint8_t> buffer(proto.size());
- size_t pos = 0;
- auto iter = proto.data();
- while (iter.readBuffer() != NULL) {
- size_t toRead = iter.currentToRead();
- std::memcpy(&buffer[pos], iter.readBuffer(), toRead);
- pos += toRead;
- iter.rp()->move(toRead);
+ if (outData != nullptr) {
+ outData->clear();
+ outData->resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
}
-
- return buffer;
}
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
@@ -151,42 +179,34 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
mMetricsManagers.erase(it);
mUidMap->OnConfigRemoved(key);
}
- auto flushTime = mLastFlushTimes.find(key);
- if (flushTime != mLastFlushTimes.end()) {
- mLastFlushTimes.erase(flushTime);
- }
+
+ std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
+ mLastBroadcastTimes.erase(key);
}
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;
- }
- }
+ std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
size_t totalBytes = metricsManager->byteSize();
- if (totalBytes > kMaxSerializedBytes) {
- flush();
- mLastFlushTimes[key] = std::move(timestampNs);
+ if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data.
+ auto lastFlushNs = mLastBroadcastTimes.find(key);
+ if (lastFlushNs != mLastBroadcastTimes.end()) {
+ if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) {
+ return;
+ }
+ }
+ mLastBroadcastTimes[key] = timestampNs;
+ ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str());
+ mSendBroadcast(key);
+ } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
+ // We ignore the return value so we force each metric producer to clear its contents.
+ metricsManager->onDumpReport();
+ ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str());
}
}
-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;
- const int numBytes = logReport.ByteSize();
- vector<uint8_t> logReportBuffer(numBytes);
- logReport.SerializeToArray(&logReportBuffer[0], numBytes);
- mPushLog(logReportBuffer);
- */
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index f38d71550eda..510dc5145dfe 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -32,8 +32,8 @@ namespace statsd {
class StatsLogProcessor : public ConfigListener {
public:
- StatsLogProcessor(const sp<UidMap>& uidMap,
- const std::function<void(const vector<uint8_t>&)>& pushLog);
+ StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor,
+ const std::function<void(const ConfigKey&)>& sendBroadcast);
virtual ~StatsLogProcessor();
virtual void OnLogEvent(const LogEvent& event);
@@ -41,18 +41,24 @@ public:
void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
void OnConfigRemoved(const ConfigKey& key);
- vector<uint8_t> onDumpReport(const ConfigKey& key);
-
- /* Request a flush through a binder call. */
- void flush();
+ size_t GetMetricsSize(const ConfigKey& key);
+
+ void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData);
+ void onAnomalyAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet);
private:
+ mutable mutex mBroadcastTimesMutex;
+
std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;
- std::unordered_map<ConfigKey, long> mLastFlushTimes;
+ std::unordered_map<ConfigKey, long> mLastBroadcastTimes;
sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
+ sp<AnomalyMonitor> mAnomalyMonitor;
+
/* Max *serialized* size of the logs kept in memory before flushing through binder call.
Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
@@ -60,17 +66,18 @@ private:
*/
static const size_t kMaxSerializedBytes = 16 * 1024;
- /* 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. */
+ /* Check if we should send a broadcast if approaching memory limits and if we're over, we
+ * actually delete the data. */
void flushIfNecessary(uint64_t timestampNs,
const ConfigKey& key,
const unique_ptr<MetricsManager>& metricsManager);
- std::function<void(const vector<uint8_t>&)> mPushLog;
+ // Function used to send a broadcast so that receiver for the config key can call getData
+ // to retrieve the stored data.
+ std::function<void(const ConfigKey& key)> mSendBroadcast;
- /* Minimum period between two flushes in nanoseconds. Currently set to 10
- * minutes. */
- static const unsigned long long kMinFlushPeriod = 600 * NS_PER_SEC;
+ /* Minimum period between two broadcasts in nanoseconds. Currently set to 60 seconds. */
+ static const unsigned long long kMinBroadcastPeriod = 60 * NS_PER_SEC;
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index a0b234087fd0..1a056df8e367 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -17,6 +17,7 @@
#define DEBUG true
#include "Log.h"
+#include "android-base/stringprintf.h"
#include "StatsService.h"
#include "config/ConfigKey.h"
#include "config/ConfigManager.h"
@@ -25,11 +26,11 @@
#include <android-base/file.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <dirent.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
#include <utils/String16.h>
-
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
@@ -42,6 +43,7 @@ namespace os {
namespace statsd {
constexpr const char* kPermissionDump = "android.permission.DUMP";
+#define STATS_SERVICE_DIR "/data/system/stats-service"
// ======================================================================
/**
@@ -71,8 +73,18 @@ StatsService::StatsService(const sp<Looper>& handlerLooper)
{
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(mUidMap, [](const vector<uint8_t>& log) {
- // TODO: Update how we send data out of StatsD.
+ mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, [this](const ConfigKey& key) {
+ auto sc = getStatsCompanionService();
+ auto receiver = mConfigManager->GetConfigReceiver(key);
+ if (sc == nullptr) {
+ ALOGD("Could not find StatsCompanionService");
+ } else if (receiver.first.size() == 0) {
+ ALOGD("Statscompanion could not find a broadcast receiver for %s",
+ key.ToString().c_str());
+ } else {
+ sc->sendBroadcast(String16(receiver.first.c_str()),
+ String16(receiver.second.c_str()));
+ }
});
mConfigManager->AddListener(mProcessor);
@@ -204,7 +216,15 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>&
}
if (!args[0].compare(String8("send-broadcast"))) {
- return cmd_trigger_broadcast(args);
+ return cmd_trigger_broadcast(out, args);
+ }
+
+ if (!args[0].compare(String8("print-stats"))) {
+ return cmd_print_stats(out);
+ }
+
+ if (!args[0].compare(String8("clear-config"))) {
+ return cmd_remove_config_files(out);
}
}
@@ -223,7 +243,12 @@ void StatsService::print_cmd_help(FILE* out) {
fprintf(out, " Prints the UID, app name, version mapping.\n");
fprintf(out, "\n");
fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmds stats pull-source [int] \n");
+ fprintf(out, "usage: adb shell cmd stats clear-config \n");
+ fprintf(out, "\n");
+ fprintf(out, " Removes all configs from disk.\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
fprintf(out, "\n");
fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
fprintf(out, "\n");
@@ -248,16 +273,62 @@ void StatsService::print_cmd_help(FILE* out) {
fprintf(out, " NAME The name of the configuration\n");
fprintf(out, "\n");
fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmd stats send-broadcast PACKAGE CLASS\n");
- fprintf(out, " Send a broadcast that triggers one subscriber to fetch metrics.\n");
- fprintf(out, " PACKAGE The name of the package to receive the broadcast.\n");
- fprintf(out, " CLASS The name of the class to receive the broadcast.\n");
+ fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
+ fprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\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");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats print-stats\n");
+ fprintf(out, " Prints some basic stats.\n");
}
-status_t StatsService::cmd_trigger_broadcast(Vector<String8>& args) {
- auto sc = getStatsCompanionService();
- sc->sendBroadcast(String16(args[1]), String16(args[2]));
- ALOGD("StatsService::trigger broadcast succeeded");
+status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
+ string name;
+ bool good = false;
+ int uid;
+ const int argCount = args.size();
+ if (argCount == 2) {
+ // Automatically pick the UID
+ uid = IPCThreadState::self()->getCallingUid();
+ // TODO: What if this isn't a binder call? Should we fail?
+ 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
+ // impersonate other uids.
+ if (mEngBuild) {
+ const char* s = args[1].c_str();
+ if (*s != '\0') {
+ char* end = NULL;
+ uid = strtol(s, &end, 0);
+ if (*end == '\0') {
+ name.assign(args[2].c_str(), args[2].size());
+ good = true;
+ }
+ }
+ } else {
+ fprintf(out,
+ "The metrics can only be dumped for other UIDs on eng or userdebug "
+ "builds.\n");
+ }
+ }
+ if (!good) {
+ print_cmd_help(out);
+ return UNKNOWN_ERROR;
+ }
+ auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name));
+ sp<IStatsCompanionService> sc = getStatsCompanionService();
+ if (sc != nullptr) {
+ sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
+ ALOGD("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
+ args[2].c_str());
+ } else {
+ ALOGD("Could not access statsCompanion");
+ }
+
return NO_ERROR;
}
@@ -362,7 +433,8 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String
}
}
if (good) {
- mProcessor->onDumpReport(ConfigKey(uid, name));
+ vector<uint8_t> data;
+ mProcessor->onDumpReport(ConfigKey(uid, name), &data);
// TODO: print the returned StatsLogReport to file instead of printing to logcat.
fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
fprintf(out, "See the StatsLogReport in logcat...\n");
@@ -378,6 +450,15 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String
}
}
+status_t StatsService::cmd_print_stats(FILE* out) {
+ vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
+ for (const ConfigKey& key : configs) {
+ fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
+ mProcessor->GetMetricsSize(key));
+ }
+ return NO_ERROR;
+}
+
status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) {
long msec = 0;
@@ -405,6 +486,27 @@ status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>
return UNKNOWN_ERROR;
}
+status_t StatsService::cmd_remove_config_files(FILE* out) {
+ fprintf(out, "Trying to remove config files...\n");
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
+ if (dir == NULL) {
+ fprintf(out, "No existing config files found exiting...\n");
+ return NO_ERROR;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') continue;
+ string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name);
+ fprintf(out, "Deleting file %s\n", file_name.c_str());
+ if (remove(file_name.c_str())) {
+ fprintf(out, "Error deleting file %s\n", file_name.c_str());
+ }
+ }
+ return NO_ERROR;
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
const vector<String16>& app) {
if (DEBUG) ALOGD("StatsService::informAllUidData was called");
@@ -452,7 +554,10 @@ Status StatsService::informAnomalyAlarmFired() {
if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired succeeded");
// TODO: check through all counters/timers and see if an anomaly has indeed occurred.
-
+ uint64_t currentTimeNs = time(nullptr) * NS_PER_SEC;
+ std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet =
+ mAnomalyMonitor->onAlarmFired(currentTimeNs);
+ mProcessor->onAnomalyAlarmFired(currentTimeNs, anomalySet);
return Status::ok();
}
@@ -541,12 +646,14 @@ void StatsService::OnLogEvent(const LogEvent& event) {
mProcessor->OnLogEvent(event);
}
-Status StatsService::getData(const String16& key, vector<uint8_t>* output) {
+Status StatsService::getData(const String16& key, vector <uint8_t>* output) {
IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump),
- reinterpret_cast<int32_t*>(ipc->getCallingPid()),
- reinterpret_cast<int32_t*>(ipc->getCallingUid()))) {
- // TODO: Implement this.
+ ALOGD("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(),
+ ipc->getCallingUid());
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ string keyStr = string(String8(key).string());
+ ConfigKey configKey(ipc->getCallingUid(), keyStr);
+ mProcessor->onDumpReport(configKey, output);
return Status::ok();
} else {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
@@ -558,11 +665,9 @@ Status StatsService::addConfiguration(const String16& key,
const String16& package, const String16& cls,
bool* success) {
IPCThreadState* ipc = IPCThreadState::self();
- int32_t* uid = reinterpret_cast<int32_t*>(ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump),
- reinterpret_cast<int32_t*>(ipc->getCallingPid()), uid)) {
+ if (checkCallingPermission(String16(kPermissionDump))) {
string keyString = string(String8(key).string());
- ConfigKey configKey(*uid, keyString);
+ ConfigKey configKey(ipc->getCallingUid(), keyString);
StatsdConfig cfg;
cfg.ParseFromArray(&config[0], config.size());
mConfigManager->UpdateConfig(configKey, cfg);
@@ -577,10 +682,9 @@ Status StatsService::addConfiguration(const String16& key,
Status StatsService::removeConfiguration(const String16& key, bool* success) {
IPCThreadState* ipc = IPCThreadState::self();
- if (checkCallingPermission(String16(kPermissionDump),
- reinterpret_cast<int32_t*>(ipc->getCallingPid()),
- reinterpret_cast<int32_t*>(ipc->getCallingUid()))) {
- // TODO: Implement this.
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ string keyStr = string(String8(key).string());
+ mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr));
return Status::ok();
} else {
*success = false;
@@ -588,13 +692,7 @@ Status StatsService::removeConfiguration(const String16& key, bool* success) {
}
}
-void StatsService::binderDied(const wp<IBinder>& who) {
- for (size_t i = 0; i < mCallbacks.size(); i++) {
- if (IInterface::asBinder(mCallbacks[i]) == who) {
- mCallbacks.removeAt(i);
- break;
- }
- }
+void StatsService::binderDied(const wp <IBinder>& who) {
}
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index c3729deabb4b..4d768f630362 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -24,7 +24,6 @@
#include "packages/UidMap.h"
#include <android/os/BnStatsManager.h>
-#include <android/os/IStatsCallbacks.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
#include <utils/Looper.h>
@@ -124,7 +123,7 @@ private:
/**
* Trigger a broadcast.
*/
- status_t cmd_trigger_broadcast(Vector<String8>& args);
+ status_t cmd_trigger_broadcast(FILE* out, Vector<String8>& args);
/**
* Handle the config sub-command.
@@ -132,6 +131,11 @@ private:
status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
/**
+ * Prints some basic stats to std out.
+ */
+ status_t cmd_print_stats(FILE* out);
+
+ /**
* Print the event log.
*/
status_t cmd_print_stats_log(FILE* out, const Vector<String8>& args);
@@ -152,6 +156,11 @@ private:
status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
/**
+ * Removes all configs stored on disk.
+ */
+ status_t cmd_remove_config_files(FILE* out);
+
+ /**
* Update a configuration.
*/
void set_config(int uid, const string& name, const StatsdConfig& config);
@@ -185,16 +194,6 @@ private:
* Whether this is an eng build.
*/
bool mEngBuild;
-
- /**
- * Lock for callback handling.
- */
- std::mutex mLock;
-
- /**
- * Vector maintaining the list of callbacks for clients.
- */
- Vector< sp<IStatsCallbacks> > mCallbacks;
};
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
index 7a464104fb09..da52a9d66494 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
@@ -129,6 +129,11 @@ int64_t AnomalyMonitor::secToMs(uint32_t timeSec) {
return ((int64_t)timeSec) * 1000;
}
+unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::onAlarmFired(
+ uint64_t timestampNs) {
+ return popSoonerThan(static_cast<uint32_t>(timestampNs));
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h
index d9207e99dd6f..0bd5055631b6 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANOMALY_MONITOR_H
-#define ANOMALY_MONITOR_H
+#pragma once
#include "anomaly/indexed_priority_queue.h"
@@ -23,6 +22,8 @@
#include <utils/RefBase.h>
#include <queue>
+#include <set>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -114,6 +115,8 @@ public:
return mRegisteredAlarmTimeSec;
}
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> onAlarmFired(uint64_t timestampNs);
+
private:
std::mutex mLock;
@@ -154,5 +157,3 @@ private:
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // ANOMALY_MONITOR_H
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
new file mode 100644
index 000000000000..0904a04987e7
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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 "AnomalyTracker.h"
+
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+AnomalyTracker::AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs)
+ : mAlert(alert),
+ mBucketSizeNs(bucketSizeNs),
+ mNumOfPastPackets(mAlert.number_of_buckets() - 1) {
+ VLOG("AnomalyTracker() called");
+ if (mAlert.number_of_buckets() <= 0) {
+ ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
+ (long long)mAlert.number_of_buckets());
+ return;
+ }
+ if (mBucketSizeNs <= 0) {
+ ALOGE("Cannot create DiscreteAnomalyTracker with bucket size %lld ",
+ (long long)mBucketSizeNs);
+ return;
+ }
+ if (!mAlert.has_trigger_if_sum_gt()) {
+ ALOGE("Cannot create DiscreteAnomalyTracker without threshold");
+ return;
+ }
+ reset(); // initialization
+}
+
+AnomalyTracker::~AnomalyTracker() {
+ VLOG("~AnomalyTracker() called");
+ stopAllAlarms();
+}
+
+void AnomalyTracker::reset() {
+ VLOG("reset() called.");
+ stopAllAlarms();
+ mPastBuckets.clear();
+ // Excludes the current bucket.
+ mPastBuckets.resize(mNumOfPastPackets);
+ mSumOverPastBuckets.clear();
+ mMostRecentBucketNum = -1;
+ mLastAlarmTimestampNs = -1;
+}
+
+size_t AnomalyTracker::index(int64_t bucketNum) const {
+ return bucketNum % mNumOfPastPackets;
+}
+
+void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) {
+ VLOG("addPastBucket() called.");
+ if (latestPastBucketNum <= mMostRecentBucketNum - mNumOfPastPackets) {
+ ALOGE("Cannot add a past bucket %lld units in past", (long long)latestPastBucketNum);
+ return;
+ }
+
+ // The past packets are ancient. Empty out old mPastBuckets[i] values and reset
+ // mSumOverPastBuckets.
+ if (latestPastBucketNum - mMostRecentBucketNum >= mNumOfPastPackets) {
+ mPastBuckets.clear();
+ mPastBuckets.resize(mNumOfPastPackets);
+ mSumOverPastBuckets.clear();
+ } else {
+ for (int64_t i = std::max(0LL, (long long)(mMostRecentBucketNum - mNumOfPastPackets + 1));
+ i <= latestPastBucketNum - mNumOfPastPackets; i++) {
+ const int idx = index(i);
+ subtractBucketFromSum(mPastBuckets[idx]);
+ mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
+ }
+ }
+
+ // It is an update operation.
+ if (latestPastBucketNum <= mMostRecentBucketNum &&
+ latestPastBucketNum > mMostRecentBucketNum - mNumOfPastPackets) {
+ subtractBucketFromSum(mPastBuckets[index(latestPastBucketNum)]);
+ }
+}
+
+void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+ const int64_t& bucketNum) {
+ flushPastBuckets(bucketNum);
+
+ auto& bucket = mPastBuckets[index(bucketNum)];
+ if (bucket == nullptr) {
+ bucket = std::make_shared<DimToValMap>();
+ }
+ bucket->insert({key, bucketValue});
+ addBucketToSum(bucket);
+ mMostRecentBucketNum = std::max(mMostRecentBucketNum, bucketNum);
+}
+
+void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucketValues,
+ const int64_t& bucketNum) {
+ VLOG("addPastBucket() called.");
+ flushPastBuckets(bucketNum);
+ // Replace the oldest bucket with the new bucket we are adding.
+ mPastBuckets[index(bucketNum)] = bucketValues;
+ addBucketToSum(bucketValues);
+ mMostRecentBucketNum = std::max(mMostRecentBucketNum, bucketNum);
+}
+
+void AnomalyTracker::subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, subtract its value from its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ auto itr = mSumOverPastBuckets.find(keyValuePair.first);
+ if (itr == mSumOverPastBuckets.end()) {
+ continue;
+ }
+ itr->second -= keyValuePair.second;
+ // TODO: No need to look up the object twice like this. Use a var.
+ if (itr->second == 0) {
+ mSumOverPastBuckets.erase(itr);
+ }
+ }
+}
+
+void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, add its value to its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
+ }
+}
+
+int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+ const int64_t& bucketNum) const {
+ const auto& bucket = mPastBuckets[index(bucketNum)];
+ if (bucket == nullptr) {
+ return 0;
+ }
+ const auto& itr = bucket->find(key);
+ return itr == bucket->end() ? 0 : itr->second;
+}
+
+int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+ const auto& itr = mSumOverPastBuckets.find(key);
+ if (itr != mSumOverPastBuckets.end()) {
+ return itr->second;
+ }
+ return 0;
+}
+
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
+ const DimToValMap& currentBucket) {
+ if (currentBucketNum > mMostRecentBucketNum + 1) {
+ addPastBucket(nullptr, currentBucketNum - 1);
+ }
+ for (auto itr = currentBucket.begin(); itr != currentBucket.end(); itr++) {
+ if (itr->second + getSumOverPastBuckets(itr->first) > mAlert.trigger_if_sum_gt()) {
+ return true;
+ }
+ }
+ // In theory, we also need to check the dimsions not in the current bucket. In single-thread
+ // mode, usually we could avoid the following loops.
+ for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
+ if (itr->second > mAlert.trigger_if_sum_gt()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+ const int64_t& currentBucketValue) {
+ if (currentBucketNum > mMostRecentBucketNum + 1) {
+ addPastBucket(key, 0, currentBucketNum - 1);
+ }
+ return getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
+}
+
+void AnomalyTracker::declareAnomaly(const uint64_t& timestamp) {
+ if (mLastAlarmTimestampNs >= 0 &&
+ timestamp - mLastAlarmTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC) {
+ VLOG("Skipping anomaly check since within refractory period");
+ return;
+ }
+ // TODO(guardrail): Consider guarding against too short refractory periods.
+ mLastAlarmTimestampNs = timestamp;
+
+ if (mAlert.has_incidentd_details()) {
+ // TODO: Can construct a name based on the criteria (and/or relay the criteria).
+ ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
+ // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
+ } else {
+ ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
+ }
+}
+
+void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+ const uint64_t& timestamp) {
+ auto itr = mAlarms.find(dimensionKey);
+ if (itr == mAlarms.end()) {
+ return;
+ }
+
+ if (itr->second != nullptr &&
+ static_cast<uint32_t>(timestamp / NS_PER_SEC) >= itr->second->timestampSec) {
+ declareAnomaly(timestamp);
+ stopAlarm(dimensionKey);
+ }
+}
+
+void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp,
+ const int64_t& currBucketNum,
+ const HashableDimensionKey& key,
+ const int64_t& currentBucketValue) {
+ if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
+ declareAnomaly(timestamp);
+ }
+}
+
+void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp,
+ const int64_t& currBucketNum,
+ const DimToValMap& currentBucket) {
+ if (detectAnomaly(currBucketNum, currentBucket)) {
+ declareAnomaly(timestamp);
+ }
+}
+
+void AnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
+ const uint64_t& timestamp) {
+ sp<const AnomalyAlarm> alarm = new AnomalyAlarm{static_cast<uint32_t>(timestamp / NS_PER_SEC)};
+ mAlarms.insert({dimensionKey, alarm});
+ if (mAnomalyMonitor != nullptr) {
+ mAnomalyMonitor->add(alarm);
+ }
+}
+
+void AnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+ auto itr = mAlarms.find(dimensionKey);
+ if (itr != mAlarms.end()) {
+ mAlarms.erase(dimensionKey);
+ }
+ if (mAnomalyMonitor != nullptr) {
+ mAnomalyMonitor->remove(itr->second);
+ }
+}
+
+void AnomalyTracker::stopAllAlarms() {
+ std::set<HashableDimensionKey> keys;
+ for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
+ keys.insert(itr->first);
+ }
+ for (auto key : keys) {
+ stopAlarm(key);
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
new file mode 100644
index 000000000000..ce6c99518cda
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -0,0 +1,156 @@
+/*
+ * 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 <gtest/gtest_prod.h>
+#include "AnomalyMonitor.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+#include "stats_util.h" // HashableDimensionKey and DimToValMap
+
+#include <memory> // unique_ptr
+#include <stdlib.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::shared_ptr;
+
+// This anomaly track assmues that all values are non-negative.
+class AnomalyTracker : public virtual RefBase {
+public:
+ AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs);
+
+ virtual ~AnomalyTracker();
+
+ // Adds a bucket.
+ // Bucket index starts from 0.
+ void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
+ void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+ const int64_t& bucketNum);
+
+ // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
+ bool detectAnomaly(const int64_t& currBucketNum, const DimToValMap& currentBucket);
+ bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
+ const int64_t& currentBucketValue);
+
+ // Informs incidentd about the detected alert.
+ void declareAnomaly(const uint64_t& timestamp);
+
+ // Detects the alert and informs the incidentd when applicable.
+ void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+ const DimToValMap& currentBucket);
+ void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+ const HashableDimensionKey& key,
+ const int64_t& currentBucketValue);
+
+ // Starts the alarm at the given timestamp.
+ void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+ // Stops the alarm.
+ void stopAlarm(const HashableDimensionKey& dimensionKey);
+
+ // Stop all the alarms owned by this tracker.
+ void stopAllAlarms();
+
+ // Init the anmaly monitor which is shared across anomaly trackers.
+ inline void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
+ mAnomalyMonitor = anomalyMonitor;
+ }
+
+ // Declares the anomaly when the alarm expired given the current timestamp.
+ void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+ const uint64_t& timestamp);
+
+ // Helper function to return the sum value of past buckets at given dimension.
+ int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+
+ // Helper function to return the value for a past bucket.
+ int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+
+ // Returns the anomaly threshold.
+ inline int64_t getAnomalyThreshold() const {
+ return mAlert.trigger_if_sum_gt();
+ }
+
+ // Helper function to return the last alarm timestamp.
+ inline int64_t getLastAlarmTimestampNs() const {
+ return mLastAlarmTimestampNs;
+ }
+
+ inline int getNumOfPastPackets() const {
+ return mNumOfPastPackets;
+ }
+
+protected:
+ void flushPastBuckets(const int64_t& currBucketNum);
+ // statsd_config.proto Alert message that defines this tracker.
+ const Alert mAlert;
+
+ // Bucket duration in ns.
+ int64_t mBucketSizeNs = 0;
+
+ // The number of past packets to track in the anomaly detection.
+ int mNumOfPastPackets = 0;
+
+ // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
+ // are still active.
+ std::unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+
+ // Anomaly alarm monitor.
+ sp<AnomalyMonitor> mAnomalyMonitor;
+
+ // The exisiting bucket list.
+ std::vector<shared_ptr<DimToValMap>> mPastBuckets;
+
+ // Sum over all existing buckets cached in mPastBuckets.
+ DimToValMap mSumOverPastBuckets;
+
+ // The bucket number of the last added bucket.
+ int64_t mMostRecentBucketNum = -1;
+
+ // The timestamp when the last anomaly was declared.
+ int64_t mLastAlarmTimestampNs = -1;
+
+ // Add the information in the given bucket to mSumOverPastBuckets.
+ void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
+
+ // Subtract the information in the given bucket from mSumOverPastBuckets
+ // and remove any items with value 0.
+ void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
+
+ // Calculates the corresponding bucket index within the circular array.
+ size_t index(int64_t bucketNum) const;
+
+ // Resets all data. For use when all the data gets stale.
+ void reset();
+
+ FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
+ FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
+ FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+ FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
+ FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
deleted file mode 100644
index 64921779acdc..000000000000
--- a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
+++ /dev/null
@@ -1,153 +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 DEBUG true // STOPSHIP if true
-#include "Log.h"
-
-#include "DiscreteAnomalyTracker.h"
-
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-DiscreteAnomalyTracker::DiscreteAnomalyTracker(const Alert& alert) : mAlert(alert) {
- VLOG("DiscreteAnomalyTracker() called");
- if (mAlert.number_of_buckets() <= 0) {
- ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
- (long long)mAlert.number_of_buckets());
- return;
- }
- mPastBuckets.resize(mAlert.number_of_buckets());
- reset(); // initialization
-}
-
-DiscreteAnomalyTracker::~DiscreteAnomalyTracker() {
- VLOG("~DiscreteAnomalyTracker() called");
-}
-
-void DiscreteAnomalyTracker::reset() {
- VLOG("reset() called.");
- mPastBuckets.clear();
- mPastBuckets.resize(mAlert.number_of_buckets());
- mSumOverPastBuckets.clear();
- mCurrentBucketIndex = -1;
- mLastAlarmAtBucketIndex = -1;
- mAnomalyDeclared = 0;
-}
-
-size_t DiscreteAnomalyTracker::index(int64_t bucketNum) {
- return bucketNum % mAlert.number_of_buckets();
-}
-
-void DiscreteAnomalyTracker::addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues,
- int64_t bucketIndex) {
- VLOG("addPastBucket() called.");
- if (bucketIndex <= mCurrentBucketIndex - mAlert.number_of_buckets()) {
- ALOGE("Cannot add a past bucket %lld units in past", (long long)bucketIndex);
- return;
- }
-
- // Empty out old mPastBuckets[i] values and update mSumOverPastBuckets.
- if (bucketIndex - mCurrentBucketIndex >= mAlert.number_of_buckets()) {
- mPastBuckets.clear();
- mPastBuckets.resize(mAlert.number_of_buckets());
- mSumOverPastBuckets.clear();
- } else {
- for (int64_t i = std::max(
- 0LL, (long long)(mCurrentBucketIndex - mAlert.number_of_buckets() + 1));
- i < bucketIndex - mAlert.number_of_buckets(); i++) {
- const int idx = index(i);
- subtractBucketFromSum(mPastBuckets[idx]);
- mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
- }
- }
- subtractBucketFromSum(mPastBuckets[index(bucketIndex)]);
- mPastBuckets[index(bucketIndex)] = nullptr; // release (but not clear) the old bucket.
-
- // Replace the oldest bucket with the new bucket we are adding.
- mPastBuckets[index(bucketIndex)] = BucketValues;
- addBucketToSum(BucketValues);
-
- mCurrentBucketIndex = std::max(mCurrentBucketIndex, bucketIndex);
-}
-
-void DiscreteAnomalyTracker::subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- // For each dimension present in the bucket, subtract its value from its corresponding sum.
- for (const auto& keyValuePair : *bucket) {
- auto itr = mSumOverPastBuckets.find(keyValuePair.first);
- if (itr == mSumOverPastBuckets.end()) {
- continue;
- }
- itr->second -= keyValuePair.second;
- // TODO: No need to look up the object twice like this. Use a var.
- if (itr->second == 0) {
- mSumOverPastBuckets.erase(itr);
- }
- }
-}
-
-void DiscreteAnomalyTracker::addBucketToSum(const shared_ptr<const DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- // For each dimension present in the bucket, add its value to its corresponding sum.
- for (const auto& keyValuePair : *bucket) {
- mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
- }
-}
-
-bool DiscreteAnomalyTracker::detectAnomaly() {
- for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
- if (mAlert.has_trigger_if_sum_gt() && itr->second > mAlert.trigger_if_sum_gt()) {
- return true;
- }
- }
- return false;
-}
-
-void DiscreteAnomalyTracker::declareAndDeclareAnomaly() {
- if (detectAnomaly()) {
- declareAnomaly();
- }
-}
-
-void DiscreteAnomalyTracker::declareAnomaly() {
- if (mLastAlarmAtBucketIndex >= 0 && mCurrentBucketIndex - mLastAlarmAtBucketIndex <=
- (long long)mAlert.refractory_period_in_buckets()) {
- VLOG("Skipping anomaly check since within refractory period");
- return;
- }
- mAnomalyDeclared++;
- // TODO(guardrail): Consider guarding against too short refractory periods.
- mLastAlarmAtBucketIndex = mCurrentBucketIndex;
-
- if (mAlert.has_incidentd_details()) {
- // TODO: Can construct a name based on the criteria (and/or relay the criteria).
- ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
- // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
- } else {
- ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
deleted file mode 100644
index ed7d5d715a6d..000000000000
--- a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
+++ /dev/null
@@ -1,91 +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.
- */
-
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-#include "stats_util.h" // HashableDimensionKey and DimToValMap
-
-#include <memory> // unique_ptr
-#include <stdlib.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-using std::shared_ptr;
-
-// This anomaly track assmues that all values are non-negative.
-class DiscreteAnomalyTracker {
- public:
- DiscreteAnomalyTracker(const Alert& alert);
-
- virtual ~DiscreteAnomalyTracker();
-
- // Adds a new bucket or updates an existing bucket.
- // Bucket index starts from 0.
- void addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, int64_t bucketIndex);
-
- // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
- bool detectAnomaly();
-
- // Informs incidentd about the detected alert.
- void declareAnomaly();
-
- // Detects the alert and informs the incidentd when applicable.
- void declareAndDeclareAnomaly();
-
-private:
- // statsd_config.proto Alert message that defines this tracker.
- const Alert mAlert;
-
- // The exisiting bucket list.
- std::vector<shared_ptr<const DimToValMap>> mPastBuckets;
-
- // Sum over all existing buckets cached in mPastBuckets.
- DimToValMap mSumOverPastBuckets;
-
- // Current bucket index of the current anomaly detection window. Bucket index starts from 0.
- int64_t mCurrentBucketIndex = -1;
-
- // The bucket index when the last anomaly was declared.
- int64_t mLastAlarmAtBucketIndex = -1;
-
- // The total number of declared anomalies.
- int64_t mAnomalyDeclared = 0;
-
- // Add the information in the given bucket to mSumOverPastBuckets.
- void addBucketToSum(const shared_ptr<const DimToValMap>& bucket);
-
- // Subtract the information in the given bucket from mSumOverPastBuckets
- // and remove any items with value 0.
- void subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket);
-
- // Calculates the corresponding bucket index within the circular array.
- size_t index(int64_t bucketNum);
-
- // Resets all data. For use when all the data gets stale.
- void reset();
-
- FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
- FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h
index 1a2e9c2a41ed..4982d4b90b7d 100644
--- a/cmds/statsd/src/anomaly/indexed_priority_queue.h
+++ b/cmds/statsd/src/anomaly/indexed_priority_queue.h
@@ -16,8 +16,6 @@
#pragma once
-#include "Log.h"
-
#include <utils/RefBase.h>
#include <unordered_map>
#include <vector>
@@ -28,7 +26,7 @@ namespace android {
namespace os {
namespace statsd {
-/** Defines a hash function for sp<AA>, returning the hash of the underlying pointer. */
+/** Defines a hash function for sp<const AA>, returning the hash of the underlying pointer. */
template <class AA>
struct SpHash {
size_t operator()(const sp<const AA>& k) const {
@@ -39,7 +37,7 @@ struct SpHash {
/**
* Min priority queue for generic type AA.
* Unlike a regular priority queue, this class is also capable of removing interior elements.
- * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning
+ * @tparam Comparator must implement [bool operator()(sp< AA> a, sp< AA> b)], returning
* whether a should be closer to the top of the queue than b.
*/
template <class AA, class Comparator>
@@ -104,7 +102,6 @@ void indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) {
if (!contains(a)) return;
size_t idx = indices[a];
if (idx >= pq.size()) {
- ALOGE("indexed_priority_queue: Invalid index in map of indices.");
return;
}
if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
@@ -193,7 +190,6 @@ void indexed_priority_queue<AA, Comparator>::sift_down(size_t idx) {
template <class AA, class Comparator>
bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const {
if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
- ALOGE("indexed_priority_queue: Attempting to access invalid index");
return false; // got to do something.
}
return Comparator()(pq[idx1], pq[idx2]);
@@ -208,7 +204,6 @@ bool indexed_priority_queue<AA, Comparator>::contains(sp<const AA> a) const {
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) {
if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
- ALOGE("indexed_priority_queue: Attempting to swap invalid index");
return;
}
sp<const AA> val_i = pq[i];
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 48b85c9dd534..57a92b61a5c9 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -73,6 +73,7 @@ message Atom {
SettingChanged setting_changed = 41;
ActivityForegroundStateChanged activity_foreground_state_changed = 42;
IsolatedUidChanged isolated_uid_changed = 43;
+ PacketWakeupOccurred packet_wakeup_occurred = 44;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -102,8 +103,7 @@ message WorkSource {
/*
* *****************************************************************************
- * Below are all of the individual atoms that are logged by Android via statsd
- * and Westworld.
+ * Below are all of the individual atoms that are logged by Android via statsd.
*
* RULES:
* - The field ids for each atom must start at 1, and count upwards by 1.
@@ -907,3 +907,32 @@ message CpuTimePerUidFreqPulled {
optional uint64 freq_idx = 2;
optional uint64 time_ms = 3;
}
+
+/*
+ * Logs the reception of an incoming network packet causing the main system to wake up for
+ * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd
+ * and processed by WakeupController.cpp.
+ */
+message PacketWakeupOccurred {
+ // The uid owning the socket into which the packet was delivered, or -1 if the packet was
+ // delivered nowhere.
+ optional int32 uid = 1;
+ // The interface name on which the packet was received.
+ optional string iface = 2;
+ // The ethertype value of the packet.
+ optional int32 ethertype = 3;
+ // String representation of the destination MAC address of the packet.
+ optional string destination_hardware_address = 4;
+ // String representation of the source address of the packet if this was an IP packet.
+ optional string source_ip = 5;
+ // String representation of the destination address of the packet if this was an IP packet.
+ optional string destination_ip = 6;
+ // The value of the protocol field if this was an IPv4 packet or the value of the Next Header
+ // field if this was an IPv6 packet. The range of possible values is the same for both IP
+ // families.
+ optional int32 ip_next_header = 7;
+ // The source port if this was a TCP or UDP packet.
+ optional int32 source_port = 8;
+ // The destination port if this was a TCP or UDP packet.
+ optional int32 destination_port = 9;
+}
diff --git a/cmds/statsd/src/atoms_copy.proto b/cmds/statsd/src/atoms_copy.proto
index 598c2bc76671..58e225a317f0 100644
--- a/cmds/statsd/src/atoms_copy.proto
+++ b/cmds/statsd/src/atoms_copy.proto
@@ -104,8 +104,7 @@ message WorkSource {
/*
* *****************************************************************************
- * Below are all of the individual atoms that are logged by Android via statsd
- * and Westworld.
+ * Below are all of the individual atoms that are logged by Android via statsd.
*
* RULES:
* - The field ids for each atom must start at 1, and count upwards by 1.
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index a694dbf754ef..60060fe60fbd 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -206,7 +206,7 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
vector<bool>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
- VLOG("Yes, already evaluated, %s %d", mName.c_str(), mNonSlicedConditionState);
+ VLOG("Yes, already evaluated, %s %d", mName.c_str(), conditionCache[mIndex]);
return;
}
@@ -230,8 +230,23 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
}
if (matchedState < 0) {
+ // The event doesn't match this condition. So we just report existing condition values.
conditionChangedCache[mIndex] = false;
- conditionCache[mIndex] = mNonSlicedConditionState;
+ if (mSliced) {
+ // if the condition result is sliced. metrics won't directly get value from the
+ // cache, so just set any value other than kNotEvaluated.
+ conditionCache[mIndex] = ConditionState::kUnknown;
+ } else if (mSlicedConditionState.find(DEFAULT_DIMENSION_KEY) ==
+ mSlicedConditionState.end()) {
+ // condition not sliced, but we haven't seen the matched start or stop yet. so return
+ // initial value.
+ conditionCache[mIndex] = mInitialValue;
+ } else {
+ // return the cached condition.
+ conditionCache[mIndex] = mSlicedConditionState[DEFAULT_DIMENSION_KEY] > 0
+ ? ConditionState::kTrue
+ : ConditionState::kFalse;
+ }
return;
}
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 2618a217c378..669a4b77f3b4 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -86,6 +86,9 @@ ConditionState evaluateCombinationCondition(const std::vector<int>& children,
case LogicalOperation::NOR:
newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
break;
+ case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+ newCondition = ConditionState::kFalse;
+ break;
}
return newCondition;
}
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
index bbf20fd1acf7..3489c43c8052 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -78,7 +78,7 @@ inline ostream& operator<<(ostream& os, const ConfigKey& config) {
/**
* A hash function for ConfigKey so it can be used for unordered_map/set.
- * Unfortunately this hast to go in std namespace because C++ is fun!
+ * Unfortunately this has to go in std namespace because C++ is fun!
*/
namespace std {
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index a9ce4a337590..21256097a766 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -18,16 +18,23 @@
#include "stats_util.h"
-#include <vector>
-
+#include <android-base/file.h>
+#include <dirent.h>
#include <stdio.h>
+#include <vector>
+#include "android-base/stringprintf.h"
namespace android {
namespace os {
namespace statsd {
+#define STATS_SERVICE_DIR "/data/system/stats-service"
+
static StatsdConfig build_fake_config();
+using android::base::StringPrintf;
+using std::unique_ptr;
+
ConfigManager::ConfigManager() {
}
@@ -35,11 +42,10 @@ ConfigManager::~ConfigManager() {
}
void ConfigManager::Startup() {
- // TODO: Implement me -- read from storage and call onto all of the listeners.
- // Instead, we'll just make a fake one.
+ readConfigFromDisk();
// this should be called from StatsService when it receives a statsd_config
- UpdateConfig(ConfigKey(0, "fake"), build_fake_config());
+ UpdateConfig(ConfigKey(1000, "fake"), build_fake_config());
}
void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
@@ -52,7 +58,7 @@ void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& confi
// Why doesn't this work? mConfigs.insert({key, config});
// Save to disk
- update_saved_configs();
+ update_saved_configs(key, config);
// Tell everyone
for (auto& listener : mListeners) {
@@ -74,15 +80,33 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) {
// Remove from map
mConfigs.erase(it);
- // Save to disk
- update_saved_configs();
-
// Tell everyone
for (auto& listener : mListeners) {
listener->OnConfigRemoved(key);
}
}
- // If we didn't find it, just quietly ignore it.
+
+ // Remove from disk. There can still be a lingering file on disk so we check
+ // whether or not the config was on memory.
+ remove_saved_configs(key);
+}
+
+void ConfigManager::remove_saved_configs(const ConfigKey& key) {
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
+ if (dir == NULL) {
+ ALOGD("no default config on disk");
+ return;
+ }
+ string prefix = StringPrintf("%d-%s", key.GetUid(), key.GetName().c_str());
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] != '.' && strncmp(name, prefix.c_str(), prefix.size()) == 0) {
+ if (remove(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str()) != 0) {
+ ALOGD("no file found");
+ }
+ }
+ }
}
void ConfigManager::RemoveConfigs(int uid) {
@@ -108,18 +132,101 @@ void ConfigManager::RemoveConfigs(int uid) {
}
}
+vector<ConfigKey> ConfigManager::GetAllConfigKeys() {
+ vector<ConfigKey> ret;
+ for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) {
+ ret.push_back(it->first);
+ }
+ return ret;
+}
+
+const pair<string, string> ConfigManager::GetConfigReceiver(const ConfigKey& key) {
+ auto it = mConfigReceivers.find(key);
+ if (it == mConfigReceivers.end()) {
+ return pair<string,string>();
+ } else {
+ return it->second;
+ }
+}
+
void ConfigManager::Dump(FILE* out) {
fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
fprintf(out, " uid name\n");
for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin();
it != mConfigs.end(); it++) {
fprintf(out, " %6d %s\n", it->first.GetUid(), it->first.GetName().c_str());
+ auto receiverIt = mConfigReceivers.find(it->first);
+ if (receiverIt != mConfigReceivers.end()) {
+ fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(),
+ receiverIt->second.second.c_str());
+ }
// TODO: Print the contents of the config too.
}
}
-void ConfigManager::update_saved_configs() {
- // TODO: Implement me -- write to disk.
+void ConfigManager::readConfigFromDisk() {
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
+ if (dir == NULL) {
+ ALOGD("no default config on disk");
+ return;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') continue;
+ ALOGD("file %s", name);
+
+ int index = 0;
+ int uid = 0;
+ string configName;
+ char* substr = strtok(name, "-");
+ // Timestamp lives at index 2 but we skip parsing it as it's not needed.
+ while (substr != nullptr && index < 2) {
+ if (index) {
+ uid = atoi(substr);
+ } else {
+ configName = substr;
+ }
+ index++;
+ }
+ if (index < 2) continue;
+ string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name);
+ ALOGD("full file %s", file_name.c_str());
+ int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ string content;
+ if (android::base::ReadFdToString(fd, &content)) {
+ StatsdConfig config;
+ if (config.ParseFromString(content)) {
+ mConfigs[ConfigKey(uid, configName)] = config;
+ ALOGD("map key uid=%d|name=%s", uid, name);
+ }
+ }
+ close(fd);
+ }
+ }
+}
+
+void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) {
+ mkdir(STATS_SERVICE_DIR, S_IRWXU);
+
+ // If there is a pre-existing config with same key we should first delete it.
+ remove_saved_configs(key);
+
+ // Then we save the latest config.
+ string file_name = StringPrintf("%s/%d-%s-%ld", STATS_SERVICE_DIR, key.GetUid(),
+ key.GetName().c_str(), time(nullptr));
+ int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ if (fd != -1) {
+ const int numBytes = config.ByteSize();
+ vector<uint8_t> buffer(numBytes);
+ config.SerializeToArray(&buffer[0], numBytes);
+ int result = write(fd, &buffer[0], numBytes);
+ close(fd);
+ bool wroteKey = (result == numBytes);
+ ALOGD("wrote to file %d", wroteKey);
+ }
}
static StatsdConfig build_fake_config() {
@@ -161,7 +268,7 @@ static StatsdConfig build_fake_config() {
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
// Anomaly threshold for screen-on count.
- Alert* alert = config.add_alerts();
+ Alert* alert = config.add_alert();
alert->set_name("1");
alert->set_number_of_buckets(6);
alert->set_trigger_if_sum_gt(10);
@@ -176,7 +283,7 @@ static StatsdConfig build_fake_config() {
keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
// Anomaly threshold for background count.
- alert = config.add_alerts();
+ alert = config.add_alert();
alert->set_name("2");
alert->set_number_of_buckets(4);
alert->set_trigger_if_sum_gt(30);
@@ -208,7 +315,7 @@ static StatsdConfig build_fake_config() {
DurationMetric* durationMetric = config.add_duration_metric();
durationMetric->set_name("5");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
@@ -222,7 +329,7 @@ static StatsdConfig build_fake_config() {
durationMetric = config.add_duration_metric();
durationMetric->set_name("6");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
@@ -236,7 +343,7 @@ static StatsdConfig build_fake_config() {
durationMetric = config.add_duration_metric();
durationMetric->set_name("7");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
@@ -248,7 +355,7 @@ static StatsdConfig build_fake_config() {
durationMetric = config.add_duration_metric();
durationMetric->set_name("8");
durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
durationMetric->set_what("SCREEN_IS_ON");
// Value metric to count KERNEL_WAKELOCK when screen turned on
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index c21247f97967..01d7fb969230 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -46,9 +46,7 @@ public:
virtual ~ConfigManager();
/**
- * Call to load the saved configs from disk.
- *
- * TODO: Implement me
+ * Initialize ConfigListener by reading from disk and get updates.
*/
void Startup();
@@ -70,6 +68,16 @@ public:
void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls);
/**
+ * Returns the package name and class name representing the broadcast receiver for this config.
+ */
+ const pair<string, string> GetConfigReceiver(const ConfigKey& key);
+
+ /**
+ * Returns all config keys registered.
+ */
+ vector<ConfigKey> GetAllConfigKeys();
+
+ /**
* Erase any broadcast receiver associated with this config key.
*/
void RemoveConfigReceiver(const ConfigKey& key);
@@ -95,7 +103,12 @@ private:
/**
* Save the configs to disk.
*/
- void update_saved_configs();
+ void update_saved_configs(const ConfigKey& key, const StatsdConfig& config);
+
+ /**
+ * Remove saved configs from disk.
+ */
+ void remove_saved_configs(const ConfigKey& key);
/**
* The Configs that have been set. Each config should
@@ -112,6 +125,11 @@ private:
* The ConfigListeners that will be told about changes.
*/
vector<sp<ConfigListener>> mListeners;
+
+ /**
+ * Call to load the saved configs from disk.
+ */
+ void readConfigFromDisk();
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index e004d217a966..e2745d21af3d 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -71,10 +71,9 @@ bool CpuTimePerUidFreqPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>
do {
timeMs = std::stoull(pch);
auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ_PULLED, timestamp);
- auto elemList = ptr->GetAndroidLogEventList();
- *elemList << uid;
- *elemList << idx;
- *elemList << timeMs;
+ ptr->write(uid);
+ ptr->write(idx);
+ ptr->write(timeMs);
ptr->init();
data->push_back(ptr);
VLOG("uid %lld, freq idx %d, sys time %lld", (long long)uid, idx, (long long)timeMs);
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
index b84b877a0ee9..e0572dca679d 100644
--- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -66,10 +66,9 @@ bool CpuTimePerUidPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* da
uint64_t sysTimeMs = std::stoull(pch);
auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_PULLED, timestamp);
- auto elemList = ptr->GetAndroidLogEventList();
- *elemList << uid;
- *elemList << userTimeMs;
- *elemList << sysTimeMs;
+ ptr->write(uid);
+ ptr->write(userTimeMs);
+ ptr->write(sysTimeMs);
ptr->init();
data->push_back(ptr);
VLOG("uid %lld, user time %lld, sys time %lld", (long long)uid, (long long)userTimeMs, (long long)sysTimeMs);
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
index 319feef49b35..3ee636dfcde5 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
@@ -93,11 +93,10 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven
auto statePtr = make_shared<LogEvent>(
android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp);
- auto elemList = statePtr->GetAndroidLogEventList();
- *elemList << state.name;
- *elemList << state.residencyInMsecSinceBoot;
- *elemList << state.totalTransitions;
- *elemList << state.supportedOnlyInSuspend;
+ statePtr->write(state.name);
+ statePtr->write(state.residencyInMsecSinceBoot);
+ statePtr->write(state.totalTransitions);
+ statePtr->write(state.supportedOnlyInSuspend);
statePtr->init();
data->push_back(statePtr);
VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
@@ -106,11 +105,10 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven
for (auto voter : state.voters) {
auto voterPtr =
make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp);
- auto elemList = voterPtr->GetAndroidLogEventList();
- *elemList << state.name;
- *elemList << voter.name;
- *elemList << voter.totalTimeInMsecVotedForSinceBoot;
- *elemList << voter.totalNumberOfTimesVotedSinceBoot;
+ voterPtr->write(state.name);
+ voterPtr->write(voter.name);
+ voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
+ voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
voterPtr->init();
data->push_back(voterPtr);
VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
@@ -141,13 +139,12 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven
const PowerStateSubsystemSleepState& state = subsystem.states[j];
auto subsystemStatePtr = make_shared<LogEvent>(
android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp);
- auto elemList = subsystemStatePtr->GetAndroidLogEventList();
- *elemList << subsystem.name;
- *elemList << state.name;
- *elemList << state.residencyInMsecSinceBoot;
- *elemList << state.totalTransitions;
- *elemList << state.lastEntryTimestampMs;
- *elemList << state.supportedOnlyInSuspend;
+ subsystemStatePtr->write(subsystem.name);
+ subsystemStatePtr->write(state.name);
+ subsystemStatePtr->write(state.residencyInMsecSinceBoot);
+ subsystemStatePtr->write(state.totalTransitions);
+ subsystemStatePtr->write(state.lastEntryTimestampMs);
+ subsystemStatePtr->write(state.supportedOnlyInSuspend);
subsystemStatePtr->init();
data->push_back(subsystemStatePtr);
VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 913b906986c3..103213830914 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -29,21 +29,71 @@ using std::ostringstream;
using std::string;
using android::util::ProtoOutputStream;
-// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
-// for strings is not cleared before we can read them.
-LogEvent::LogEvent(log_msg& msg) : mList(msg) {
- init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
+LogEvent::LogEvent(log_msg& msg) {
+ mContext =
+ create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
+ mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
+ init(mContext);
}
-LogEvent::LogEvent(int tag, uint64_t timestampNs) : mList(tag), mTimestampNs(timestampNs) {
+LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) {
+ mTimestampNs = timestampNs;
+ mTagId = tagId;
+ mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
+ if (mContext) {
+ android_log_write_int32(mContext, tagId);
+ }
}
-LogEvent::~LogEvent() {
+void LogEvent::init() {
+ if (mContext) {
+ const char* buffer;
+ size_t len = android_log_write_list_buffer(mContext, &buffer);
+ // turns to reader mode
+ mContext = create_android_log_parser(buffer, len);
+ init(mContext);
+ }
}
-void LogEvent::init() {
- mList.convert_to_reader();
- init(mTimestampNs, &mList);
+bool LogEvent::write(int32_t value) {
+ if (mContext) {
+ return android_log_write_int32(mContext, value) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(uint32_t value) {
+ if (mContext) {
+ return android_log_write_int32(mContext, value) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(uint64_t value) {
+ if (mContext) {
+ return android_log_write_int64(mContext, value) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(const string& value) {
+ if (mContext) {
+ return android_log_write_string8_len(mContext, value.c_str(), value.length()) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(float value) {
+ if (mContext) {
+ return android_log_write_float32(mContext, value) >= 0;
+ }
+ return false;
+}
+
+LogEvent::~LogEvent() {
+ if (mContext) {
+ android_log_destroy(&mContext);
+ }
}
/**
@@ -51,22 +101,25 @@ void LogEvent::init() {
* The goal is to do as little preprocessing as possible, because we read a tiny fraction
* of the elements that are written to the log.
*/
-void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
- mTimestampNs = timestampNs;
- mTagId = reader->tag();
-
+void LogEvent::init(android_log_context context) {
mElements.clear();
android_log_list_element elem;
-
// TODO: The log is actually structured inside one list. This is convenient
// because we'll be able to use it to put the attribution (WorkSource) block first
// without doing our own tagging scheme. Until that change is in, just drop the
// list-related log elements and the order we get there is our index-keyed data
// structure.
+ int i = 0;
do {
- elem = android_log_read_next(reader->context());
+ elem = android_log_read_next(context);
switch ((int)elem.type) {
case EVENT_TYPE_INT:
+ // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. If we add WorkSource, it would
+ // be the list starting at [2].
+ if (i == 1) {
+ mTagId = elem.data.int32;
+ break;
+ }
case EVENT_TYPE_FLOAT:
case EVENT_TYPE_STRING:
case EVENT_TYPE_LONG:
@@ -81,13 +134,10 @@ void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
default:
break;
}
+ i++;
} while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
}
-android_log_event_list* LogEvent::GetAndroidLogEventList() {
- return &mList;
-}
-
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
if (key < 1 || (key - 1) >= mElements.size()) {
*err = BAD_INDEX;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 298494049628..7e8a96b2114e 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -21,6 +21,7 @@
#include <android/util/ProtoOutputStream.h>
#include <log/log_event_list.h>
#include <log/log_read.h>
+#include <private/android_logger.h>
#include <utils/Errors.h>
#include <memory>
@@ -45,12 +46,9 @@ public:
explicit LogEvent(log_msg& msg);
/**
- * Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
- * mode. Obtain this list with the getter. Make sure to call init() before attempting to read
- * any of the values. This constructor is useful for unit-testing since we can't pass in an
- * android_log_event_list since there is no copy constructor or assignment operator available.
+ * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
*/
- explicit LogEvent(int tag, uint64_t timestampNs);
+ explicit LogEvent(int32_t tagId, uint64_t timestampNs);
~LogEvent();
@@ -76,6 +74,17 @@ public:
float GetFloat(size_t key, status_t* err) const;
/**
+ * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
+ * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
+ */
+ bool write(uint32_t value);
+ bool write(int32_t value);
+ bool write(uint64_t value);
+ bool write(int64_t value);
+ bool write(const string& value);
+ bool write(float value);
+
+ /**
* Return a string representation of this event.
*/
string ToString() const;
@@ -91,13 +100,6 @@ public:
KeyValuePair GetKeyValueProto(size_t key) const;
/**
- * A pointer to the contained log_event_list.
- *
- * @return The android_log_event_list contained within.
- */
- android_log_event_list* GetAndroidLogEventList();
-
- /**
* Used with the constructor where tag is passed in. Converts the log_event_list to read mode
* and prepares the list for reading.
*/
@@ -113,16 +115,11 @@ private:
/**
* Parses a log_msg into a LogEvent object.
*/
- void init(const log_msg& msg);
-
- /**
- * Parses a log_msg into a LogEvent object.
- */
- void init(int64_t timestampNs, android_log_event_list* reader);
+ void init(android_log_context context);
vector<android_log_list_element> mElements;
- // Need a copy of the android_log_event_list so the strings are not cleared.
- android_log_event_list mList;
+
+ android_log_context mContext;
uint64_t mTimestampNs;
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index cccc9b3317e7..f7352cd1190c 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -84,6 +84,9 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera
}
}
break;
+ case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+ matched = false;
+ break;
}
return matched;
}
@@ -112,6 +115,7 @@ bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& e
if (err == NO_ERROR && val != NULL) {
if (!(cur.eq_string() == val)) {
allMatched = false;
+ break;
}
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt ||
@@ -126,26 +130,30 @@ bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& e
if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt) {
if (!(val == cur.eq_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt) {
if (!(val < cur.lt_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt) {
if (!(val > cur.gt_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt) {
if (!(val <= cur.lte_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) {
if (!(val >= cur.gte_int())) {
allMatched = false;
+ break;
}
}
}
- break;
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) {
// Boolean fields
status_t err = NO_ERROR;
@@ -153,21 +161,24 @@ bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& e
if (err == NO_ERROR) {
if (!(cur.eq_bool() == val)) {
allMatched = false;
+ break;
}
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat ||
matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
// Float fields
status_t err = NO_ERROR;
- bool val = event.GetFloat(key, &err);
+ float val = event.GetFloat(key, &err);
if (err == NO_ERROR) {
if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat) {
- if (!(cur.lt_float() <= val)) {
+ if (!(val < cur.lt_float())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
- if (!(cur.gt_float() >= val)) {
+ if (!(val > cur.gt_float())) {
allMatched = false;
+ break;
}
}
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index f9da68e8e5c5..d47bd4fe9e44 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -17,7 +17,6 @@
#define DEBUG true // STOPSHIP if true
#include "Log.h"
-#include "../anomaly/DiscreteAnomalyTracker.h"
#include "CountMetricProducer.h"
#include "stats_util.h"
@@ -114,7 +113,7 @@ std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::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.
- flushCounterIfNeeded(endTime);
+ flushIfNeeded(endTime);
VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& counter : mPastBuckets) {
@@ -170,7 +169,6 @@ std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
startNewProtoOutputStream(endTime);
mPastBuckets.clear();
- mByteSize = 0;
return buffer;
@@ -188,7 +186,7 @@ void CountMetricProducer::onMatchedLogEventInternal(
const LogEvent& event, bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
- flushCounterIfNeeded(eventTimeNs);
+ flushIfNeeded(eventTimeNs);
if (condition == false) {
return;
@@ -205,41 +203,41 @@ void CountMetricProducer::onMatchedLogEventInternal(
count++;
}
- VLOG("metric %s %s->%d", mMetric.name().c_str(), eventKey.c_str(),
- (*mCurrentSlicedCounter)[eventKey]);
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+ mCurrentSlicedCounter->find(eventKey)->second);
+ }
+
+ VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(),
+ (long long)(*mCurrentSlicedCounter)[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
-void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
+void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+ if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return;
}
- // adjust the bucket start time
- // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64.
- uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum;
for (const auto& counter : *mCurrentSlicedCounter) {
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
- VLOG("metric %s, dump key value: %s -> %d", mMetric.name().c_str(), counter.first.c_str(),
- counter.second);
- mByteSize += sizeof(info);
+ VLOG("metric %s, dump key value: %s -> %lld", mMetric.name().c_str(), counter.first.c_str(),
+ (long long)counter.second);
}
for (auto& tracker : mAnomalyTrackers) {
- tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum);
- tracker->declareAndDeclareAnomaly();
+ tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
}
// Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
mCurrentSlicedCounter = std::make_shared<DimToValMap>();
-
+ uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
mCurrentBucketNum += numBucketsForward;
VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
@@ -250,7 +248,11 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
// greater than actual data size as it contains each dimension of
// CountMetricData is duplicated.
size_t CountMetricProducer::byteSize() {
- return mByteSize;
+ size_t totalSize = 0;
+ for (const auto& pair : mPastBuckets) {
+ totalSize += pair.second.size() * kBucketSize;
+ }
+ return totalSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b7e480c4e78d..b3f8ee37379e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -21,9 +21,9 @@
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
+#include "../anomaly/AnomalyTracker.h"
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
-#include "../anomaly/DiscreteAnomalyTracker.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -36,6 +36,7 @@ struct CountBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mCount;
+ uint64_t mBucketNum;
};
class CountMetricProducer : public MetricProducer {
@@ -50,6 +51,8 @@ public:
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override;
+
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -76,18 +79,15 @@ private:
// TODO: Add a lock to mPastBuckets.
std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
- size_t mByteSize;
-
// The current bucket.
std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
- vector<std::unique_ptr<DiscreteAnomalyTracker>> mAnomalyTrackers;
-
- void flushCounterIfNeeded(const uint64_t newEventTime);
+ static const size_t kBucketSize = sizeof(CountBucket{});
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
+ FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index aaf3ec2c298b..b0a97b143400 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -63,6 +63,7 @@ const int FIELD_ID_DURATION = 3;
DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex,
+ const bool nesting,
const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension,
const uint64_t startTimeNs)
@@ -71,6 +72,7 @@ DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
mStartIndex(startIndex),
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
+ mNested(nesting),
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
@@ -108,16 +110,16 @@ void DurationMetricProducer::startNewProtoOutputStream(long long startTime) {
}
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
- vector<DurationBucket>& 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);
+ const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
+ switch (mMetric.aggregation_type()) {
+ case DurationMetric_AggregationType_SUM:
+ return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
+ mNested, mCurrentBucketStartTimeNs,
+ mBucketSizeNs, mAnomalyTrackers, bucket);
+ case DurationMetric_AggregationType_MAX_SPARSE:
+ return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
+ mNested, mCurrentBucketStartTimeNs,
+ mBucketSizeNs, mAnomalyTrackers, bucket);
}
}
@@ -129,7 +131,6 @@ void DurationMetricProducer::finish() {
void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
// Now for each of the on-going event, check if the condition has changed for them.
- flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
pair.second->onSlicedConditionMayChange(eventTime);
}
@@ -140,27 +141,11 @@ void DurationMetricProducer::onConditionChanged(const bool conditionMet, const u
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) {
pair.second->onConditionChanged(conditionMet, eventTime);
}
}
-static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper& wrapper,
- const vector<KeyValuePair>& key,
- const vector<DurationBucketInfo>& buckets) {
- DurationMetricData* 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] duration(ns): %lld", bucket.start_bucket_nanos(),
- bucket.end_bucket_nanos(), bucket.duration_nanos());
- }
-}
-
std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
@@ -220,10 +205,8 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
(long long)mCurrentBucketStartTimeNs);
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
-
startNewProtoOutputStream(endTime);
- mPastBuckets.clear();
-
+ // TODO: Properly clear the old buckets.
return buffer;
}
@@ -231,17 +214,19 @@ void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
return;
}
-
VLOG("flushing...........");
- for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) {
+ for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
if (it->second->flushIfNeeded(eventTime)) {
VLOG("erase bucket for key %s", it->first.c_str());
- mCurrentSlicedDuration.erase(it);
+ it = mCurrentSlicedDuration.erase(it);
+ } else {
+ ++it;
}
}
int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
}
void DurationMetricProducer::onMatchedLogEventInternal(
@@ -260,7 +245,7 @@ void DurationMetricProducer::onMatchedLogEventInternal(
HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
- mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]);
+ mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
}
auto it = mCurrentSlicedDuration.find(eventKey);
@@ -268,7 +253,7 @@ void DurationMetricProducer::onMatchedLogEventInternal(
if (matcherIndex == mStartIndex) {
it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
} else if (matcherIndex == mStopIndex) {
- it->second->noteStop(atomKey, event.GetTimestampNs());
+ it->second->noteStop(atomKey, event.GetTimestampNs(), false);
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index eea00454d5e6..4c8dbcb83a6b 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef DURATION_METRIC_PRODUCER_H
-#define DURATION_METRIC_PRODUCER_H
+#pragma once
+
#include <unordered_map>
@@ -39,7 +39,8 @@ 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 bool nesting,
+ const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
virtual ~DurationMetricProducer();
@@ -47,6 +48,7 @@ public:
void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -80,6 +82,9 @@ private:
// Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
const size_t mStopAllIndex;
+ // nest counting -- for the same key, stops must match the number of starts to make real stop
+ const bool mNested;
+
// The dimension from the atom predicate. e.g., uid, wakelock name.
const vector<KeyMatcher> mInternalDimension;
@@ -91,11 +96,8 @@ private:
std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDuration;
- void flushDurationIfNeeded(const uint64_t newEventTime);
-
- std::unique_ptr<DurationTracker> createDurationTracker(std::vector<DurationBucket>& bucket);
-
- void flushIfNeeded(uint64_t timestamp);
+ std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
+ std::vector<DurationBucket>& bucket);
static const size_t kBucketSize = sizeof(DurationBucket{});
};
@@ -104,4 +106,3 @@ private:
} // namespace os
} // namespace android
-#endif // DURATION_METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 74ba40ba20ae..9a94a0efa850 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -96,7 +96,6 @@ std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() {
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
- mByteSize = 0;
return buffer;
}
@@ -121,11 +120,10 @@ void EventMetricProducer::onMatchedLogEventInternal(
event.ToProto(*mProto);
mProto->end(eventToken);
mProto->end(wrapperToken);
- // TODO: Find a proper way to derive the size of incoming LogEvent.
}
size_t EventMetricProducer::byteSize() {
- return mByteSize;
+ return mProto->bytesWritten();
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 77406216f421..0c453cdac99b 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -47,6 +47,8 @@ public:
void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override {
+ }
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -65,8 +67,6 @@ protected:
private:
const EventMetric mMetric;
-
- size_t mByteSize;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index ed18f897943e..42ac1a2bbd7b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -63,8 +63,9 @@ const int FIELD_ID_END_BUCKET_NANOS = 2;
const int FIELD_ID_GAUGE = 3;
GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId)
- : MetricProducer((time(nullptr) * NS_PER_SEC), conditionIndex, wizard),
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const int64_t startTimeNs)
+ : MetricProducer(startTimeNs, conditionIndex, wizard),
mMetric(metric),
mPullTagId(pullTagId) {
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
@@ -114,7 +115,7 @@ std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::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.
- flushGaugeIfNeededLocked(time(nullptr) * NS_PER_SEC);
+ flushIfNeeded(time(nullptr) * NS_PER_SEC);
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
@@ -168,7 +169,6 @@ std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
- mByteSize = 0;
return buffer;
@@ -178,15 +178,18 @@ std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
AutoMutex _l(mLock);
VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ flushIfNeeded(eventTime);
mCondition = conditionMet;
- // Push mode. Nothing to do.
+ // Push mode. No need to proactively pull the gauge data.
if (mPullTagId == -1) {
return;
}
- // If (1) the condition is not met or (2) we already pulled the gauge metric in the current
- // bucket, do not pull gauge again.
- if (!mCondition || mCurrentSlicedBucket.size() > 0) {
+ if (!mCondition) {
+ return;
+ }
+ // Already have gauge metric for the current bucket, do not do it again.
+ if (mCurrentSlicedBucket->size() > 0) {
return;
}
vector<std::shared_ptr<LogEvent>> allData;
@@ -197,16 +200,16 @@ void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, false /*scheduledPull*/);
}
- flushGaugeIfNeededLocked(eventTime);
+ flushIfNeeded(eventTime);
}
void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
-long GaugeMetricProducer::getGauge(const LogEvent& event) {
+int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
status_t err = NO_ERROR;
- long val = event.GetLong(mMetric.gauge_field(), &err);
+ int64_t val = event.GetLong(mMetric.gauge_field(), &err);
if (err == NO_ERROR) {
return val;
} else {
@@ -217,14 +220,9 @@ long GaugeMetricProducer::getGauge(const LogEvent& event) {
void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
AutoMutex mutex(mLock);
- if (allData.size() == 0) {
- return;
- }
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, true /*scheduledPull*/);
}
- uint64_t eventTime = allData.at(0)->GetTimestampNs();
- flushGaugeIfNeededLocked(eventTime);
}
void GaugeMetricProducer::onMatchedLogEventInternal(
@@ -241,15 +239,21 @@ void GaugeMetricProducer::onMatchedLogEventInternal(
return;
}
- // For gauge metric, we just simply use the latest guage in the given bucket.
- const long gauge = getGauge(event);
- if (gauge < 0) {
- VLOG("Invalid gauge at event Time: %lld", (long long)eventTimeNs);
+ // When the event happens in a new bucket, flush the old buckets.
+ if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) {
+ flushIfNeeded(eventTimeNs);
+ }
+
+ // For gauge metric, we just simply use the first guage in the given bucket.
+ if (!mCurrentSlicedBucket->empty()) {
return;
}
- mCurrentSlicedBucket[eventKey] = gauge;
- if (mPullTagId < 0) {
- flushGaugeIfNeededLocked(eventTimeNs);
+ const long gauge = getGauge(event);
+ if (gauge >= 0) {
+ (*mCurrentSlicedBucket)[eventKey] = gauge;
+ }
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge);
}
}
@@ -258,39 +262,45 @@ void GaugeMetricProducer::onMatchedLogEventInternal(
// bucket.
// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
// the GaugeMetricProducer while holding the lock.
-void GaugeMetricProducer::flushGaugeIfNeededLocked(const uint64_t eventTimeNs) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
- VLOG("event time is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
- (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
+void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+ if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return;
}
- // Adjusts the bucket start time
- int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
GaugeBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum;
- for (const auto& slice : mCurrentSlicedBucket) {
+ for (const auto& slice : *mCurrentSlicedBucket) {
info.mGauge = slice.second;
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
- mByteSize += sizeof(info);
-
- VLOG("gauge metric %s, dump key value: %s -> %ld", mMetric.name().c_str(),
- slice.first.c_str(), slice.second);
+ VLOG("gauge metric %s, dump key value: %s -> %lld", mMetric.name().c_str(),
+ slice.first.c_str(), (long long)slice.second);
}
+
// Reset counters
- mCurrentSlicedBucket.clear();
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addPastBucket(mCurrentSlicedBucket, mCurrentBucketNum);
+ }
+ mCurrentSlicedBucket = std::make_shared<DimToValMap>();
+
+ // Adjusts the bucket start time
+ int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
size_t GaugeMetricProducer::byteSize() {
- return mByteSize;
+ size_t totalSize = 0;
+ for (const auto& pair : mPastBuckets) {
+ totalSize += pair.second.size() * kBucketSize;
+ }
+ return totalSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index f9e4deb9f04e..930afb21543f 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -19,6 +19,7 @@
#include <unordered_map>
#include <android/util/ProtoOutputStream.h>
+#include <gtest/gtest_prod.h>
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
@@ -35,6 +36,7 @@ struct GaugeBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mGauge;
+ uint64_t mBucketNum;
};
// This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -46,7 +48,8 @@ public:
// TODO: Pass in the start time from MetricsManager, it should be consistent
// for all metrics.
GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId);
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const int64_t startTimeNs);
virtual ~GaugeMetricProducer();
@@ -57,6 +60,7 @@ public:
void onSlicedConditionMayChange(const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -92,13 +96,15 @@ private:
std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current bucket.
- std::unordered_map<HashableDimensionKey, long> mCurrentSlicedBucket;
+ std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>();
- void flushGaugeIfNeededLocked(const uint64_t newEventTime);
+ int64_t getGauge(const LogEvent& event);
- long getGauge(const LogEvent& event);
+ static const size_t kBucketSize = sizeof(GaugeBucket{});
- size_t mByteSize;
+ FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index c0930e381ce3..0f9374429930 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,6 +17,7 @@
#ifndef METRIC_PRODUCER_H
#define METRIC_PRODUCER_H
+#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
@@ -58,17 +59,29 @@ public:
// 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.
virtual void finish() = 0;
+ virtual void flushIfNeeded(const uint64_t newEventTime) = 0;
// TODO: Pass a timestamp as a parameter in onDumpReport and update all its
// implementations.
+ // onDumpReport returns the proto-serialized output and clears the previously stored contents.
virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0;
virtual bool isConditionSliced() const {
return mConditionSliced;
};
+ // Returns the memory in bytes currently used to store this metric's data. Does not change
+ // state.
virtual size_t byteSize() = 0;
+ void addAnomalyTracker(sp<AnomalyTracker> tracker) {
+ mAnomalyTrackers.push_back(tracker);
+ }
+
+ int64_t getBuckeSizeInNs() const {
+ return mBucketSizeNs;
+ }
+
protected:
const uint64_t mStartTimeNs;
@@ -94,6 +107,8 @@ protected:
std::vector<EventConditionLink> mConditionLinks;
+ std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
+
/*
* Individual metrics can implement their own business logic here. All pre-processing is done.
*
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index e8a862fef79b..5916b040889c 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -38,8 +38,8 @@ namespace statsd {
MetricsManager::MetricsManager(const StatsdConfig& config) {
mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
- mAllMetricProducers, mConditionToMetricMap, mTrackerToMetricMap,
- mTrackerToConditionMap);
+ mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
+ mTrackerToMetricMap, mTrackerToConditionMap);
}
MetricsManager::~MetricsManager() {
@@ -150,6 +150,19 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
}
}
+void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs,
+ sp<const AnomalyAlarm> anomaly) {
+ for (const auto& itr : mAllAnomalyTrackers) {
+ itr->declareAnomaly(timestampNs);
+ }
+}
+
+void MetricsManager::setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
+ for (auto& itr : mAllAnomalyTrackers) {
+ itr->setAnomalyMonitor(anomalyMonitor);
+ }
+}
+
// Returns the total byte size of all metrics managed by a single config source.
size_t MetricsManager::byteSize() {
size_t totalSize = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 39c79f99daca..59ade7cdf48b 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,6 +16,8 @@
#pragma once
+#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "logd/LogEvent.h"
@@ -43,9 +45,15 @@ public:
// Called when everything should wrap up. We are about to finish (e.g., new config comes).
void finish();
+ void onAnomalyAlarmFired(const uint64_t timestampNs, sp<const AnomalyAlarm> anomaly);
+
+ void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
+
// Config source owner can call onDumpReport() to get all the metrics collected.
std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
+ // Computes the total byte size of all metrics managed by a single config source.
+ // Does not change the state.
size_t byteSize();
private:
@@ -68,6 +76,9 @@ private:
// Hold all metrics from the config.
std::vector<sp<MetricProducer>> mAllMetricProducers;
+ // Hold all alert trackers.
+ std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers;
+
// To make the log processing more efficient, we want to do as much filtering as possible
// before we go into individual trackers and conditions to match.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index a35070bab54b..9cbe6f6eac15 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -188,7 +188,6 @@ std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
- mByteSize = 0;
return buffer;
@@ -215,7 +214,7 @@ void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, false);
}
- flush_if_needed(eventTime);
+ flushIfNeeded(eventTime);
}
return;
}
@@ -230,12 +229,12 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
uint64_t eventTime = allData.at(0)->GetTimestampNs();
// alarm is not accurate and might drift.
if (eventTime > mCurrentBucketStartTimeNs + mBucketSizeNs * 3 / 2) {
- flush_if_needed(eventTime);
+ flushIfNeeded(eventTime);
}
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, true);
}
- flush_if_needed(eventTime);
+ flushIfNeeded(eventTime);
}
}
@@ -282,7 +281,7 @@ void ValueMetricProducer::onMatchedLogEventInternal(
}
}
} else {
- flush_if_needed(eventTimeNs);
+ flushIfNeeded(eventTimeNs);
interval.raw.push_back(make_pair(value, 0));
}
}
@@ -298,18 +297,18 @@ long ValueMetricProducer::get_value(const LogEvent& event) {
}
}
-void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
+void ValueMetricProducer::flushIfNeeded(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());
ValueBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum;
int tainted = 0;
for (const auto& slice : mCurrentSlicedBucket) {
@@ -329,23 +328,29 @@ void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
- mByteSize += sizeof(info);
}
// Reset counters
mCurrentSlicedBucket.swap(mNextSlicedBucket);
mNextSlicedBucket.clear();
+
int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+
if (numBucketsForward > 1) {
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
}
- mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
size_t ValueMetricProducer::byteSize() {
- return mByteSize;
+ size_t totalSize = 0;
+ for (const auto& pair : mPastBuckets) {
+ totalSize += pair.second.size() * kBucketSize;
+ }
+ return totalSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c6c87f5b15f6..2b0b0ada2a8f 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -33,6 +33,7 @@ struct ValueBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mValue;
+ uint64_t mBucketNum;
};
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
@@ -46,6 +47,7 @@ public:
void onConditionChanged(const bool condition, const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t eventTimeNs) override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -102,9 +104,7 @@ private:
long get_value(const LogEvent& event);
- void flush_if_needed(const uint64_t eventTimeNs);
-
- size_t mByteSize;
+ static const size_t kBucketSize = sizeof(ValueBucket{});
FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 5c76d0e6513b..c91ea0f845bd 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -17,6 +17,7 @@
#ifndef DURATION_TRACKER_H
#define DURATION_TRACKER_H
+#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "stats_util.h"
@@ -34,6 +35,10 @@ enum DurationState {
// Hold duration information for one atom level duration in current on-going bucket.
struct DurationInfo {
DurationState state;
+
+ // the number of starts seen.
+ int32_t startCount;
+
// most recent start time.
int64_t lastStartTime;
// existing duration in current bucket.
@@ -42,29 +47,37 @@ struct DurationInfo {
// cache the HashableDimensionKeys we need to query the condition for this duration event.
ConditionKey conditionKeys;
- DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
+ DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
};
struct DurationBucket {
- int64_t mBucketStartNs;
- int64_t mBucketEndNs;
- int64_t mDuration;
+ uint64_t mBucketStartNs;
+ uint64_t mBucketEndNs;
+ uint64_t mDuration;
+ uint64_t mBucketNum;
};
class DurationTracker {
public:
- DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket)
- : mWizard(wizard),
+ DurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
+ std::vector<DurationBucket>& bucket)
+ : mEventKey(eventKey),
+ mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
- mCurrentBucketStartTimeNs(currentBucketStartNs),
mBucketSizeNs(bucketSizeNs),
+ mNested(nesting),
+ mCurrentBucketStartTimeNs(currentBucketStartNs),
mBucket(bucket),
- mDuration(0){};
+ mDuration(0),
+ mCurrentBucketNum(0),
+ mAnomalyTrackers(anomalyTrackers){};
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 noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) = 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;
@@ -72,18 +85,78 @@ public:
// events, so that the owner can safely remove the tracker.
virtual bool flushIfNeeded(uint64_t timestampNs) = 0;
+ // Predict the anomaly timestamp given the current status.
+ virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const = 0;
+
protected:
+ // Starts the anomaly alarm.
+ void startAnomalyAlarm(const uint64_t eventTime) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->startAlarm(mEventKey,
+ predictAnomalyTimestampNs(*anomalyTracker, eventTime));
+ }
+ }
+ }
+
+ // Stops the anomaly alarm.
+ void stopAnomalyAlarm() {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->stopAlarm(mEventKey);
+ }
+ }
+ }
+
+ void addPastBucketToAnomalyTrackers(const int64_t& bucketValue, const int64_t& bucketNum) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->addPastBucket(mEventKey, bucketValue, bucketNum);
+ }
+ }
+ }
+
+ void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+ const int64_t& currentBucketValue) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
+ currentBucketValue);
+ }
+ }
+ }
+
+ void declareAnomalyIfAlarmExpired(const uint64_t& timestamp) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->declareAnomalyIfAlarmExpired(mEventKey, timestamp);
+ }
+ }
+ }
+
+ HashableDimensionKey mEventKey;
+
sp<ConditionWizard> mWizard;
- int mConditionTrackerIndex;
+ const int mConditionTrackerIndex;
- uint64_t mCurrentBucketStartTimeNs;
+ const int64_t mBucketSizeNs;
+
+ const bool mNested;
- int64_t mBucketSizeNs;
+ uint64_t mCurrentBucketStartTimeNs;
std::vector<DurationBucket>& mBucket; // where to write output
int64_t mDuration; // current recorded duration result
+
+ uint64_t mCurrentBucketNum;
+
+ std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
+
+ FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 43c21a830ecf..06e743daa090 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -23,14 +23,18 @@ namespace android {
namespace os {
namespace statsd {
-MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+MaxDurationTracker::MaxDurationTracker(const HashableDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& bucket)
- : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) {
+ : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
+ anomalyTrackers, bucket) {
}
void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) {
+ flushIfNeeded(eventTime);
// this will construct a new DurationInfo if this key didn't exist.
DurationInfo& duration = mInfos[key];
duration.conditionKeys = conditionKey;
@@ -38,10 +42,10 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi
switch (duration.state) {
case kStarted:
- // The same event is already started. Because we are not counting nesting, so ignore.
+ duration.startCount++;
break;
case kPaused:
- // Safe to do nothing here. Paused means started but condition is false.
+ duration.startCount++;
break;
case kStopped:
if (!condition) {
@@ -51,11 +55,16 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi
duration.state = DurationState::kStarted;
duration.lastStartTime = eventTime;
}
+ duration.startCount = 1;
break;
}
}
-void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
+
+void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ bool forceStop) {
+ flushIfNeeded(eventTime);
+ declareAnomalyIfAlarmExpired(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.
@@ -68,31 +77,47 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_
// 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);
+ duration.startCount--;
+ if (forceStop || !mNested || duration.startCount <= 0) {
+ 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;
+ duration.lastStartTime = -1;
+ VLOG(" record duration: %lld ", (long long)duration.lastDuration);
+ }
break;
}
case DurationState::kPaused: {
- duration.state = DurationState::kStopped;
+ duration.startCount--;
+ if (forceStop || !mNested || duration.startCount <= 0) {
+ duration.state = DurationState::kStopped;
+ }
break;
}
}
if (duration.lastDuration > mDuration) {
mDuration = duration.lastDuration;
+ detectAndDeclareAnomaly(eventTime, mCurrentBucketNum, mDuration);
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);
+ if (duration.state == DurationState::kStopped) {
+ mInfos.erase(key);
+ }
}
+
void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
- for (auto& pair : mInfos) {
- noteStop(pair.first, eventTime);
+ std::set<HashableDimensionKey> keys;
+ for (const auto& pair : mInfos) {
+ keys.insert(pair.first);
+ }
+ for (auto& key : keys) {
+ noteStop(key, eventTime, true);
}
}
@@ -111,7 +136,7 @@ bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) {
DurationBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = endTime;
-
+ info.mBucketNum = mCurrentBucketNum;
uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
@@ -154,6 +179,7 @@ bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) {
if (mDuration != 0) {
info.mDuration = mDuration;
mBucket.push_back(info);
+ addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
VLOG(" final duration for last bucket: %lld", (long long)mDuration);
}
@@ -163,11 +189,15 @@ bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) {
DurationBucket info;
info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i;
info.mBucketEndNs = endTime + mBucketSizeNs * i;
+ info.mBucketNum = mCurrentBucketNum + i;
info.mDuration = mBucketSizeNs;
mBucket.push_back(info);
+ addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs);
}
}
+
+ mCurrentBucketNum += numBucketsForward;
// If this tracker has no pending events, tell owner to remove.
return !hasPendingEvent;
}
@@ -193,6 +223,8 @@ void MaxDurationTracker::onConditionChanged(bool condition, const uint64_t times
void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
const uint64_t timestamp) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
auto it = mInfos.find(key);
if (it == mInfos.end()) {
return;
@@ -204,7 +236,6 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b
if (!conditionMet) {
it->second.state = DurationState::kPaused;
it->second.lastDuration += (timestamp - it->second.lastStartTime);
-
VLOG("MaxDurationTracker Key: %s Started->Paused ", key.c_str());
}
break;
@@ -221,6 +252,16 @@ void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, b
}
break;
}
+ if (it->second.lastDuration > mDuration) {
+ mDuration = it->second.lastDuration;
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ }
+}
+
+int64_t MaxDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const {
+ ALOGE("Max duration producer does not support anomaly timestamp prediction!!!");
+ return currentTimestamp;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index b095884876a8..ca1021041e34 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,22 +28,34 @@ namespace statsd {
// they stop or bucket expires.
class MaxDurationTracker : public DurationTracker {
public:
- MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ MaxDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& 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 noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) 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;
+ int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const override;
+
private:
std::map<HashableDimensionKey, DurationInfo> mInfos;
void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
const uint64_t timestamp);
+
+ FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
+ FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
+ FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
+ FRIEND_TEST(MaxDurationTrackerTest, TestStopAll);
+ FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index e4f1d2149f7c..29b6c8965ee6 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -20,10 +20,17 @@
namespace android {
namespace os {
namespace statsd {
-OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+
+using std::pair;
+
+OringDurationTracker::OringDurationTracker(const HashableDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& bucket)
- : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket),
+ : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
+ anomalyTrackers, bucket),
mStarted(),
mPaused() {
mLastStartTime = 0;
@@ -31,14 +38,16 @@ OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int condi
void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) {
+ flushIfNeeded(eventTime);
if (condition) {
if (mStarted.size() == 0) {
mLastStartTime = eventTime;
VLOG("record first start....");
+ startAnomalyAlarm(eventTime);
}
- mStarted.insert(key);
+ mStarted[key]++;
} else {
- mPaused.insert(key);
+ mPaused[key]++;
}
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
@@ -48,69 +57,98 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi
VLOG("Oring: %s start, condition %d", key.c_str(), condition);
}
-void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) {
+void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
+ const bool stopAll) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
VLOG("Oring: %s stop", key.c_str());
auto it = mStarted.find(key);
if (it != mStarted.end()) {
- mStarted.erase(it);
+ (it->second)--;
+ if (stopAll || !mNested || it->second <= 0) {
+ mStarted.erase(it);
+ mConditionKeyMap.erase(key);
+ }
if (mStarted.empty()) {
mDuration += (timestamp - mLastStartTime);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ mLastStartTime = -1;
VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
(long long)mDuration);
}
}
- mPaused.erase(key);
- mConditionKeyMap.erase(key);
+ auto pausedIt = mPaused.find(key);
+ if (pausedIt != mPaused.end()) {
+ (pausedIt->second)--;
+ if (stopAll || !mNested || pausedIt->second <= 0) {
+ mPaused.erase(pausedIt);
+ mConditionKeyMap.erase(key);
+ }
+ }
+ if (mStarted.empty()) {
+ stopAnomalyAlarm();
+ }
}
+
void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
if (!mStarted.empty()) {
mDuration += (timestamp - mLastStartTime);
VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
(long long)mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
}
+ stopAnomalyAlarm();
mStarted.clear();
mPaused.clear();
mConditionKeyMap.clear();
+ mLastStartTime = -1;
}
bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+ if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return false;
}
VLOG("OringDurationTracker Flushing.............");
// adjust the bucket start time
int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
- DurationBucket info;
- uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
- info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = endTime;
-
- uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
-
+ DurationBucket current_info;
+ current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
+ current_info.mBucketEndNs = current_info.mBucketStartNs + mBucketSizeNs;
+ current_info.mBucketNum = mCurrentBucketNum;
+ // Process the current bucket.
if (mStarted.size() > 0) {
- mDuration += (endTime - mLastStartTime);
+ mDuration += (current_info.mBucketEndNs - mLastStartTime);
+ mLastStartTime = current_info.mBucketEndNs;
}
- if (mDuration != 0) {
- info.mDuration = 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 (mDuration > 0) {
+ current_info.mDuration = mDuration;
+ mBucket.push_back(current_info);
+ addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum);
+ VLOG(" duration: %lld", (long long)current_info.mDuration);
}
if (mStarted.size() > 0) {
for (int i = 1; i < numBucketsForward; i++) {
DurationBucket info;
- info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i;
- info.mBucketEndNs = endTime + mBucketSizeNs * i;
+ info.mBucketStartNs = mCurrentBucketStartTimeNs + mBucketSizeNs * i;
+ info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum + i;
info.mDuration = mBucketSizeNs;
- mBucket.push_back(info);
- VLOG(" add filling bucket with duration %lld", (long long)mBucketSizeNs);
+ mLastStartTime = info.mBucketEndNs;
+ if (info.mDuration > 0) {
+ mBucket.push_back(info);
+ addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
+ VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
+ }
}
}
- mLastStartTime = mCurrentBucketStartTimeNs;
+ mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+
mDuration = 0;
// if all stopped, then tell owner it's safe to remove this tracker.
@@ -118,11 +156,13 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
}
void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
- vector<HashableDimensionKey> startedToPaused;
- vector<HashableDimensionKey> pausedToStarted;
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
+ vector<pair<HashableDimensionKey, int>> startedToPaused;
+ vector<pair<HashableDimensionKey, int>> pausedToStarted;
if (!mStarted.empty()) {
for (auto it = mStarted.begin(); it != mStarted.end();) {
- auto key = *it;
+ const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
++it;
@@ -130,8 +170,8 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
}
if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
ConditionState::kTrue) {
+ startedToPaused.push_back(*it);
it = mStarted.erase(it);
- startedToPaused.push_back(key);
VLOG("Key %s started -> paused", key.c_str());
} else {
++it;
@@ -139,15 +179,17 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
}
if (mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
+ mDuration = (timestamp - mLastStartTime);
+ mLastStartTime = -1;
VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
(long long)mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
}
}
if (!mPaused.empty()) {
for (auto it = mPaused.begin(); it != mPaused.end();) {
- auto key = *it;
+ const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
++it;
@@ -155,8 +197,8 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
}
if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
ConditionState::kTrue) {
+ pausedToStarted.push_back(*it);
it = mPaused.erase(it);
- pausedToStarted.push_back(key);
VLOG("Key %s paused -> started", key.c_str());
} else {
++it;
@@ -168,26 +210,95 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp)
}
}
+ if (mStarted.empty() && !pausedToStarted.empty()) {
+ startAnomalyAlarm(timestamp);
+ }
mStarted.insert(pausedToStarted.begin(), pausedToStarted.end());
mPaused.insert(startedToPaused.begin(), startedToPaused.end());
+
+ if (mStarted.empty()) {
+ stopAnomalyAlarm();
+ }
}
void OringDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
if (condition) {
if (!mPaused.empty()) {
VLOG("Condition true, all started");
if (mStarted.empty()) {
- mLastStartTime = timestamp;
+ mLastStartTime = -1;
+ }
+ if (mStarted.empty() && !mPaused.empty()) {
+ startAnomalyAlarm(timestamp);
}
mStarted.insert(mPaused.begin(), mPaused.end());
+ mPaused.clear();
}
} else {
if (!mStarted.empty()) {
VLOG("Condition false, all paused");
- mDuration += (timestamp - mLastStartTime);
+ mDuration = (timestamp - mLastStartTime);
+ mLastStartTime = -1;
mPaused.insert(mStarted.begin(), mStarted.end());
+ mStarted.clear();
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
}
}
+ if (mStarted.empty()) {
+ stopAnomalyAlarm();
+ }
+}
+
+int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t eventTimestampNs) const {
+ // TODO: Unit-test this and see if it can be done more efficiently (e.g. use int32).
+ // All variables below represent durations (not timestamps).
+
+ // The time until the current bucket ends. This is how much more 'space' it can hold.
+ const int64_t currRemainingBucketSizeNs =
+ mBucketSizeNs - (eventTimestampNs - mCurrentBucketStartTimeNs);
+ // TODO: This should never be < 0. Document/guard against possible failures if it is.
+
+ const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
+
+ // As we move into the future, old buckets get overwritten (so their old data is erased).
+
+ // Sum of past durations. Will change as we overwrite old buckets.
+ int64_t pastNs = mDuration;
+ pastNs += anomalyTracker.getSumOverPastBuckets(mEventKey);
+
+ // How much of the threshold is still unaccounted after considering pastNs.
+ int64_t leftNs = thresholdNs - pastNs;
+
+ // First deal with the remainder of the current bucket.
+ if (leftNs <= currRemainingBucketSizeNs) { // Predict the anomaly will occur in this bucket.
+ return eventTimestampNs + leftNs;
+ }
+ // The remainder of this bucket contributes, but we must then move to the next bucket.
+ pastNs += currRemainingBucketSizeNs;
+
+ // Now deal with the past buckets, starting with the oldest.
+ for (int futBucketIdx = 0; futBucketIdx < anomalyTracker.getNumOfPastPackets();
+ futBucketIdx++) {
+ // We now overwrite the oldest bucket with the previous 'current', and start a new
+ // 'current'.
+ pastNs -= anomalyTracker.getPastBucketValue(
+ mEventKey, mCurrentBucketNum - anomalyTracker.getNumOfPastPackets() + futBucketIdx);
+ leftNs = thresholdNs - pastNs;
+ if (leftNs <= mBucketSizeNs) { // Predict anomaly will occur in this bucket.
+ return eventTimestampNs + currRemainingBucketSizeNs + (futBucketIdx * mBucketSizeNs) +
+ leftNs;
+ } else { // This bucket would be entirely filled, and we'll need to move to the next
+ // bucket.
+ pastNs += mBucketSizeNs;
+ }
+ }
+
+ // If we have reached this point, we even have to overwrite the the original current bucket.
+ // Thus, none of the past data will still be extant - pastNs is now 0.
+ return eventTimestampNs + thresholdNs;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index b54dafaa1758..6f921139b76f 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,27 +27,39 @@ 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,
+ OringDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& 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 noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) 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;
+ int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const 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;
+ std::map<HashableDimensionKey, int> mStarted;
+ std::map<HashableDimensionKey, int> mPaused;
int64_t mLastStartTime;
std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
+
+ FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
+ FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
+ FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
+ FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4c63b20f5fb6..2344cb4170a3 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -190,7 +190,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
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>>& trackerToMetricMap,
+ unordered_map<string, int>& metricMap) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
config.event_metric_size() + config.value_metric_size();
@@ -208,6 +209,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
}
int metricIndex = allMetricProducers.size();
+ metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -236,6 +238,7 @@ 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);
+ metricMap.insert({metric.name(), metricIndex});
auto what_it = conditionTrackerMap.find(metric.what());
if (what_it == conditionTrackerMap.end()) {
@@ -252,6 +255,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
const auto& simpleCondition = durationWhat.simple_condition();
+ bool nesting = simpleCondition.count_nesting();
+
int trackerIndices[3] = {-1, -1, -1};
if (!simpleCondition.has_start() ||
!handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
@@ -294,7 +299,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
sp<MetricProducer> durationMetric = new DurationMetricProducer(
metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
- wizard, internalDimension, startTimeNs);
+ nesting, wizard, internalDimension, startTimeNs);
allMetricProducers.push_back(durationMetric);
}
@@ -303,6 +308,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
for (int i = 0; i < config.event_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const EventMetric& metric = config.event_metric(i);
+ metricMap.insert({metric.name(), metricIndex});
if (!metric.has_name() || !metric.has_what()) {
ALOGW("cannot find the metric name or what in config");
return false;
@@ -340,6 +346,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
}
int metricIndex = allMetricProducers.size();
+ metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -385,6 +392,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
}
int metricIndex = allMetricProducers.size();
+ metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -417,21 +425,43 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l
}
sp<MetricProducer> gaugeProducer =
- new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId);
+ new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
allMetricProducers.push_back(gaugeProducer);
}
return true;
}
+bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& metricProducerMap,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
+ for (int i = 0; i < config.alert_size(); i++) {
+ const Alert& alert = config.alert(i);
+ const auto& itr = metricProducerMap.find(alert.metric_name());
+ if (itr == metricProducerMap.end()) {
+ ALOGW("alert has unknown metric name: %s %s", alert.name().c_str(),
+ alert.metric_name().c_str());
+ return false;
+ }
+ const int metricIndex = itr->second;
+ sp<AnomalyTracker> anomalyTracker =
+ new AnomalyTracker(alert, allMetricProducers[metricIndex]->getBuckeSizeInNs());
+ allMetricProducers[metricIndex]->addAnomalyTracker(anomalyTracker);
+ allAnomalyTrackers.push_back(anomalyTracker);
+ }
+ return true;
+}
+
bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers,
unordered_map<int, std::vector<int>>& conditionToMetricMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap,
unordered_map<int, std::vector<int>>& trackerToConditionMap) {
unordered_map<string, int> logTrackerMap;
unordered_map<string, int> conditionTrackerMap;
+ unordered_map<string, int> metricProducerMap;
if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) {
ALOGE("initLogMatchingTrackers failed");
@@ -447,10 +477,14 @@ bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
allConditionTrackers, allMetricProducers, conditionToMetricMap,
- trackerToMetricMap)) {
+ trackerToMetricMap, metricProducerMap)) {
ALOGE("initMetricProducers failed");
return false;
}
+ if (!initAlerts(config, metricProducerMap, allMetricProducers, allAnomalyTrackers)) {
+ ALOGE("initAlerts failed");
+ return false;
+ }
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index edf3af0aff8e..7d7e0c36951f 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -88,6 +88,7 @@ bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index e1d0aceb3bd5..b7d8f97afaae 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -35,15 +35,14 @@ typedef std::string HashableDimensionKey;
typedef std::map<std::string, HashableDimensionKey> ConditionKey;
-// TODO: For P, change int to int64_t.
-// TODO: Should HashableDimensionKey be marked here as const?
-typedef std::unordered_map<HashableDimensionKey, int> DimToValMap;
+typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
EventMetricData parse(log_msg msg);
int getTagId(log_msg msg);
std::string getHashableKey(std::vector<KeyValuePair> key);
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 9a760b1b8ca5..e75a37f98966 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -47,6 +47,7 @@ message KeyValueMatcher {
}
enum LogicalOperation {
+ LOGICAL_OPERATION_UNSPECIFIED = 0;
AND = 1;
OR = 2;
NOT = 3;
@@ -111,23 +112,12 @@ message Bucket {
optional int64 bucket_size_millis = 1;
}
-message Alert {
- optional string name = 1;
-
- optional string metric_name = 2;
-
- message IncidentdDetails {
- repeated int32 section = 1;
- }
- optional IncidentdDetails incidentd_details = 3;
-
- optional int32 number_of_buckets = 4;
-
- optional int32 refractory_period_secs = 5;
+message EventConditionLink {
+ optional string condition = 1;
- optional int64 trigger_if_sum_gt = 6;
+ repeated KeyMatcher key_in_main = 2;
- optional int32 refractory_period_in_buckets = 7;
+ repeated KeyMatcher key_in_condition = 3;
}
message EventMetric {
@@ -151,9 +141,7 @@ message CountMetric {
optional Bucket bucket = 5;
- optional bool include_in_output = 6;
-
- repeated EventConditionLink links = 7;
+ repeated EventConditionLink links = 6;
}
message DurationMetric {
@@ -166,11 +154,11 @@ message DurationMetric {
repeated EventConditionLink links = 4;
enum AggregationType {
- DURATION_SUM = 1;
+ SUM = 1;
- DURATION_MAX_SPARSE = 2;
+ MAX_SPARSE = 2;
}
- optional AggregationType type = 5;
+ optional AggregationType aggregation_type = 5 [default = SUM];
repeated KeyMatcher dimension = 6;
@@ -208,16 +196,28 @@ message ValueMetric {
repeated EventConditionLink links = 7;
- enum Operation { SUM = 1; }
- optional Operation operation = 9 [default = SUM];
+ enum AggregationType {
+ SUM = 1;
+ }
+ optional AggregationType aggregation_type = 8 [default = SUM];
}
-message EventConditionLink {
- optional string condition = 1;
+message Alert {
+ optional string name = 1;
+
+ optional string metric_name = 2;
- repeated KeyMatcher key_in_main = 2;
- repeated KeyMatcher key_in_condition = 3;
-};
+ message IncidentdDetails {
+ repeated int32 section = 1;
+ }
+ optional IncidentdDetails incidentd_details = 3;
+
+ optional int32 number_of_buckets = 4;
+
+ optional int32 refractory_period_secs = 5;
+
+ optional int64 trigger_if_sum_gt = 6;
+}
message StatsdConfig {
optional string name = 1;
@@ -236,5 +236,5 @@ message StatsdConfig {
repeated Condition condition = 8;
- repeated Alert alerts = 9;
+ repeated Alert alert = 9;
}
diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AnomalyMonitor_test.cpp
index 59fa16007eef..920ca08ef955 100644
--- a/cmds/statsd/tests/AnomalyMonitor_test.cpp
+++ b/cmds/statsd/tests/AnomalyMonitor_test.cpp
@@ -20,6 +20,8 @@ using namespace android::os::statsd;
#ifdef __ANDROID__
TEST(AnomalyMonitor, popSoonerThan) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set;
AnomalyMonitor am(2);
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index fad5de67ca1a..f570522dcd0a 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -85,7 +85,7 @@ TEST(ConfigManagerTest, TestAddUpdateRemove) {
// TODO: Remove this when we get rid of the fake one, and make this
// test loading one from disk somewhere.
EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq("12345")))
+ OnConfigUpdated(ConfigKeyEq(1000, "fake"), StatsdConfigEq("12345")))
.RetiresOnSaturation();
manager->Startup();
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index d0898b0fa865..40c0e9d74c1d 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -27,7 +27,7 @@ using namespace android::os::statsd;
using std::unordered_map;
using std::vector;
-const int TAG_ID = 123;
+const int32_t TAG_ID = 123;
const int FIELD_ID_1 = 1;
const int FIELD_ID_2 = 2;
const int FIELD_ID_3 = 2;
@@ -43,8 +43,6 @@ TEST(LogEntryMatcherTest, TestSimpleMatcher) {
simpleMatcher->set_tag(TAG_ID);
LogEvent event(TAG_ID, 0);
-
- // Convert to a LogEvent
event.init();
// Test
@@ -63,10 +61,8 @@ TEST(LogEntryMatcherTest, TestBoolMatcher) {
// Set up the event
LogEvent event(TAG_ID, 0);
- auto list = event.GetAndroidLogEventList();
- *list << true;
- *list << false;
-
+ event.write(true);
+ event.write(false);
// Convert to a LogEvent
event.init();
@@ -99,14 +95,44 @@ TEST(LogEntryMatcherTest, TestStringMatcher) {
// Set up the event
LogEvent event(TAG_ID, 0);
- auto list = event.GetAndroidLogEventList();
- *list << "some value";
+ event.write("some value");
+ // Convert to a LogEvent
+ event.init();
+
+ // Test
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+}
+
+TEST(LogEntryMatcherTest, TestMultiFieldsMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->set_tag(TAG_ID);
+ auto keyValue1 = simpleMatcher->add_key_value_matcher();
+ keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
+ auto keyValue2 = simpleMatcher->add_key_value_matcher();
+ keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
+
+ // Set up the event
+ LogEvent event(TAG_ID, 0);
+ event.write(2);
+ event.write(3);
// Convert to a LogEvent
event.init();
// Test
+ keyValue1->set_eq_int(2);
+ keyValue2->set_eq_int(3);
EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+
+ keyValue1->set_eq_int(2);
+ keyValue2->set_eq_int(4);
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+
+ keyValue1->set_eq_int(4);
+ keyValue2->set_eq_int(3);
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
}
TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
@@ -120,9 +146,7 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
// Set up the event
LogEvent event(TAG_ID, 0);
- auto list = event.GetAndroidLogEventList();
- *list << 11;
-
+ event.write(11);
event.init();
// Test
@@ -168,8 +192,6 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
}
-#if 0
-
TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
// Set up the matcher
LogEntryMatcher matcher;
@@ -179,22 +201,28 @@ TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
auto keyValue = simpleMatcher->add_key_value_matcher();
keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
- LogEvent event;
- event.tagId = TAG_ID;
-
+ LogEvent event1(TAG_ID, 0);
keyValue->set_lt_float(10.0);
- event.floatMap[FIELD_ID_1] = 10.1;
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
- event.floatMap[FIELD_ID_1] = 9.9;
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
-
+ event1.write(10.1f);
+ event1.init();
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event1));
+
+ LogEvent event2(TAG_ID, 0);
+ event2.write(9.9f);
+ event2.init();
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, event2));
+
+ LogEvent event3(TAG_ID, 0);
+ event3.write(10.1f);
+ event3.init();
keyValue->set_gt_float(10.0);
- event.floatMap[FIELD_ID_1] = 10.1;
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
- event.floatMap[FIELD_ID_1] = 9.9;
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, event3));
+
+ LogEvent event4(TAG_ID, 0);
+ event4.write(9.9f);
+ event4.init();
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event4));
}
-#endif
// Helper for the composite matchers.
void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, int val) {
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index caa1cf4dba1b..3dd4e70ff49c 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -34,6 +34,7 @@ using android::sp;
using std::set;
using std::unordered_map;
using std::vector;
+using android::os::statsd::Condition;
#ifdef __ANDROID__
@@ -71,6 +72,19 @@ StatsdConfig buildGoodConfig() {
combination->add_matcher("SCREEN_IS_ON");
combination->add_matcher("SCREEN_IS_OFF");
+ CountMetric* metric = config.add_count_metric();
+ metric->set_name("3");
+ metric->set_what("SCREEN_IS_ON");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ KeyMatcher* keyMatcher = metric->add_dimension();
+ keyMatcher->set_key(1);
+
+ auto alert = config.add_alert();
+ alert->set_name("3");
+ alert->set_metric_name("3");
+ alert->set_number_of_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
return config;
}
@@ -100,6 +114,29 @@ StatsdConfig buildCircleMatchers() {
return config;
}
+StatsdConfig buildAlertWithUnknownMetric() {
+ StatsdConfig config;
+ config.set_name("12345");
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_name("3");
+ metric->set_what("SCREEN_IS_ON");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ KeyMatcher* keyMatcher = metric->add_dimension();
+ keyMatcher->set_key(1);
+
+ auto alert = config.add_alert();
+ alert->set_name("3");
+ alert->set_metric_name("2");
+ alert->set_number_of_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
+ return config;
+}
+
StatsdConfig buildMissingMatchers() {
StatsdConfig config;
config.set_name("12345");
@@ -156,6 +193,12 @@ StatsdConfig buildDimensionMetricsWithMultiTags() {
KeyMatcher* keyMatcher = metric->add_dimension();
keyMatcher->set_key(1);
+ auto alert = config.add_alert();
+ alert->set_name("3");
+ alert->set_metric_name("3");
+ alert->set_number_of_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
return config;
}
@@ -183,7 +226,7 @@ StatsdConfig buildCircleConditions() {
simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
- Condition* condition = config.add_condition();
+ auto condition = config.add_condition();
condition->set_name("SCREEN_IS_ON");
SimpleCondition* simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("SCREEN_IS_ON");
@@ -206,13 +249,16 @@ TEST(MetricsManagerTest, TestGoodConfig) {
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
+ EXPECT_EQ(1u, allMetricProducers.size());
+ EXPECT_EQ(1u, allAnomalyTrackers.size());
}
TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
@@ -221,13 +267,14 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
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));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -236,13 +283,14 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
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));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -251,13 +299,13 @@ TEST(MetricsManagerTest, TestMissingMatchers) {
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
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));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestCircleConditionDependency) {
@@ -266,13 +314,30 @@ TEST(MetricsManagerTest, TestCircleConditionDependency) {
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ 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, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+ StatsdConfig config = buildAlertWithUnknownMetric();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
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));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
#else
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index c64719ee0c85..0c194683884c 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -34,12 +34,12 @@ const string kApp2 = "app2.sharing.1";
TEST(UidMapTest, TestIsolatedUID) {
sp<UidMap> m = new UidMap();
- StatsLogProcessor p(m, nullptr);
+ sp<AnomalyMonitor> anomalyMonitor;
+ StatsLogProcessor p(m, anomalyMonitor, nullptr);
LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
- android_log_event_list* list = addEvent.GetAndroidLogEventList();
- *list << 100; // parent UID
- *list << 101; // isolated UID
- *list << 1; // Indicates creation.
+ addEvent.write(100); // parent UID
+ addEvent.write(101); // isolated UID
+ addEvent.write(1); // Indicates creation.
addEvent.init();
EXPECT_EQ(101, m->getParentUidOrSelf(101));
@@ -48,10 +48,9 @@ TEST(UidMapTest, TestIsolatedUID) {
EXPECT_EQ(100, m->getParentUidOrSelf(101));
LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
- list = removeEvent.GetAndroidLogEventList();
- *list << 100; // parent UID
- *list << 101; // isolated UID
- *list << 0; // Indicates removal.
+ removeEvent.write(100); // parent UID
+ removeEvent.write(101); // isolated UID
+ removeEvent.write(0); // Indicates removal.
removeEvent.init();
p.OnLogEvent(removeEvent);
EXPECT_EQ(101, m->getParentUidOrSelf(101));
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index b8150d04ed07..e0200f27316e 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/anomaly/DiscreteAnomalyTracker.h"
+#include "src/anomaly/AnomalyTracker.h"
#include <gtest/gtest.h>
#include <stdio.h>
@@ -37,7 +37,7 @@ void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair
}
}
-std::shared_ptr<DimToValMap> MockeBucket(
+std::shared_ptr<DimToValMap> MockBucket(
const std::vector<std::pair<string, long>>& key_value_pair_list) {
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
AddValueToBucket(key_value_pair_list, bucket);
@@ -45,190 +45,240 @@ std::shared_ptr<DimToValMap> MockeBucket(
}
TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
+ const int64_t bucketSizeNs = 30 * NS_PER_SEC;
Alert alert;
alert.set_number_of_buckets(3);
- alert.set_refractory_period_in_buckets(3);
+ alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
alert.set_trigger_if_sum_gt(2);
- DiscreteAnomalyTracker anomaly_tracker(alert);
-
- std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}});
- // Adds bucket #0
- anomaly_tracker.addOrUpdateBucket(bucket0, 0);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
-
- // Adds bucket #0 again. The sum does not change.
- anomaly_tracker.addOrUpdateBucket(bucket0, 0);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
-
- // Adds bucket #1.
- std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}});
- anomaly_tracker.addOrUpdateBucket(bucket1, 1);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- // Alarm.
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
-
- // Adds bucket #1 again. The sum does not change.
- anomaly_tracker.addOrUpdateBucket(bucket1, 1);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- // Alarm.
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
-
- // Adds bucket #2.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ AnomalyTracker anomalyTracker(alert, bucketSizeNs);
+
+ std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ int64_t eventTimestamp0 = 10;
+ std::shared_ptr<DimToValMap> bucket1 = MockBucket({{"a", 1}});
+ int64_t eventTimestamp1 = bucketSizeNs + 11;
+ std::shared_ptr<DimToValMap> bucket2 = MockBucket({{"b", 1}});
+ int64_t eventTimestamp2 = 2 * bucketSizeNs + 12;
+ std::shared_ptr<DimToValMap> bucket3 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp3 = 3 * bucketSizeNs + 13;
+ std::shared_ptr<DimToValMap> bucket4 = MockBucket({{"b", 1}});
+ int64_t eventTimestamp4 = 4 * bucketSizeNs + 14;
+ std::shared_ptr<DimToValMap> bucket5 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp5 = 5 * bucketSizeNs + 15;
+ std::shared_ptr<DimToValMap> bucket6 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp6 = 6 * bucketSizeNs + 16;
+
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(0, *bucket0));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp0, 0, *bucket0);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
+
+ // Adds past bucket #0
+ anomalyTracker.addPastBucket(bucket0, 0);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 1, *bucket1);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
+
+ // Adds past bucket #0 again. The sum does not change.
+ anomalyTracker.addPastBucket(bucket0, 0);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1 + 1, 1, *bucket1);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
+
+ // Adds past bucket #1.
+ anomalyTracker.addPastBucket(bucket1, 1);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 2, *bucket2);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+ // Adds past bucket #1 again. Nothing changes.
+ anomalyTracker.addPastBucket(bucket1, 1);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2 + 1, 2, *bucket2);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+ // Adds past bucket #2.
+ anomalyTracker.addPastBucket(bucket2, 2);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(3, *bucket3));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 3, *bucket3);
// Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
// Adds bucket #3.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomalyTracker.addPastBucket(bucket3, 3L);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(4, *bucket4));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 4, *bucket4);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
- // Adds bucket #3.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- // Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
-
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Adds bucket #4.
+ anomalyTracker.addPastBucket(bucket4, 4);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(5, *bucket5));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 5, *bucket5);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
+
+ // Adds bucket #5.
+ anomalyTracker.addPastBucket(bucket5, 5);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(6, *bucket6));
// Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 6, *bucket6);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
}
TEST(AnomalyTrackerTest, TestSparseBuckets) {
+ const int64_t bucketSizeNs = 30 * NS_PER_SEC;
Alert alert;
alert.set_number_of_buckets(3);
- alert.set_refractory_period_in_buckets(3);
+ alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
alert.set_trigger_if_sum_gt(2);
- DiscreteAnomalyTracker anomaly_tracker(alert);
-
- // Add bucket #9
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
-
- // Add bucket #16
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
-
- // Add bucket #18
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
+ AnomalyTracker anomalyTracker(alert, bucketSizeNs);
+
+ std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}});
+ std::shared_ptr<DimToValMap> bucket18 = MockBucket({{"b", 1}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket20 = MockBucket({{"b", 3}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket25 = MockBucket({{"d", 1}});
+ std::shared_ptr<DimToValMap> bucket28 = MockBucket({{"e", 2}});
+
+ int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
+ int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
+ int64_t eventTimestamp3 = bucketSizeNs * 17 + 1;
+ int64_t eventTimestamp4 = bucketSizeNs * 19 + 2;
+ int64_t eventTimestamp5 = bucketSizeNs * 24 + 3;
+ int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
+
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(9, *bucket9));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 9, *bucket9);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1);
+
+ // Add past bucket #9
+ anomalyTracker.addPastBucket(bucket9, 9);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(16, *bucket16));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 16, *bucket16);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+
+ // Add past bucket #16
+ anomalyTracker.addPastBucket(bucket16, 16);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(18, *bucket18));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+ // Within refractory period.
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 18, *bucket18);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+
+ // Add past bucket #18
+ anomalyTracker.addPastBucket(bucket18, 18);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 20, *bucket20);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
+
+ // Add bucket #18 again. Nothing changes.
+ anomalyTracker.addPastBucket(bucket18, 18);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4 + 1, 20, *bucket20);
// Within refractory period.
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
-
- // Add bucket #18 again.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
-
- // Add bucket #20
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
-
- // Add bucket #25
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
-
- // Add bucket #28
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
+
+ // Add past bucket #20
+ anomalyTracker.addPastBucket(bucket20, 20);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 3LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(25, *bucket25));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 25, *bucket25);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
+
+ // Add past bucket #25
+ anomalyTracker.addPastBucket(bucket25, 25);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("d"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(28, *bucket28));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 28, *bucket28);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
+
+ // Updates current bucket #28.
+ (*bucket28)["e"] = 5;
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(28, *bucket28));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6 + 7, 28, *bucket28);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp6 + 7);
}
} // namespace statsd
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 05aad290e3fe..80a0068690f7 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -46,10 +46,9 @@ SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
}
void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
- auto list = event->GetAndroidLogEventList();
- *list << uid; // uid
- *list << wl;
- *list << acquire;
+ event->write(uid); // uid
+ event->write(wl);
+ event->write(acquire);
event->init();
}
@@ -108,6 +107,18 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
EXPECT_TRUE(changedCache[0]);
+ // match nothing.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
// the case for match stop.
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index 600b95356083..d6cd876e5ad5 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -22,10 +22,12 @@ using namespace android::os::statsd;
/** struct for template in indexed_priority_queue */
struct AATest : public RefBase {
- AATest(uint32_t val) : val(val) {
+ AATest(uint32_t val, std::string a, std::string b) : val(val), a(a), b(b) {
}
const int val;
+ const std::string a;
+ const std::string b;
struct Smaller {
bool operator()(const sp<const AATest> a, const sp<const AATest> b) const {
@@ -36,9 +38,11 @@ struct AATest : public RefBase {
#ifdef __ANDROID__
TEST(indexed_priority_queue, empty_and_size) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4 = new AATest{4};
- sp<const AATest> aa8 = new AATest{8};
+ sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
EXPECT_EQ(0u, ipq.size());
EXPECT_TRUE(ipq.empty());
@@ -61,13 +65,15 @@ TEST(indexed_priority_queue, empty_and_size) {
}
TEST(indexed_priority_queue, top) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa2 = new AATest{2};
- sp<const AATest> aa4 = new AATest{4};
- sp<const AATest> aa8 = new AATest{8};
- sp<const AATest> aa12 = new AATest{12};
- sp<const AATest> aa16 = new AATest{16};
- sp<const AATest> aa20 = new AATest{20};
+ sp<const AATest> aa2 = new AATest{2, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa12 = new AATest{12, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa16 = new AATest{16, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa20 = new AATest{20, emptyMetricId, emptyDimensionId};
EXPECT_EQ(ipq.top(), nullptr);
@@ -113,9 +119,11 @@ TEST(indexed_priority_queue, top) {
}
TEST(indexed_priority_queue, push_same_aa) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4_a = new AATest{4};
- sp<const AATest> aa4_b = new AATest{4};
+ sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
ipq.push(aa4_a);
EXPECT_EQ(1u, ipq.size());
@@ -134,9 +142,11 @@ TEST(indexed_priority_queue, push_same_aa) {
}
TEST(indexed_priority_queue, remove_nonexistant) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4 = new AATest{4};
- sp<const AATest> aa5 = new AATest{5};
+ sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa5 = new AATest{5, emptyMetricId, emptyDimensionId};
ipq.push(aa4);
ipq.remove(aa5);
@@ -147,8 +157,10 @@ TEST(indexed_priority_queue, remove_nonexistant) {
TEST(indexed_priority_queue, remove_same_aa) {
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4_a = new AATest{4};
- sp<const AATest> aa4_b = new AATest{4};
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
+ sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
ipq.push(aa4_a);
ipq.push(aa4_b);
@@ -184,9 +196,11 @@ TEST(indexed_priority_queue, nulls) {
TEST(indexed_priority_queue, pop) {
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> a = new AATest{1};
- sp<const AATest> b = new AATest{2};
- sp<const AATest> c = new AATest{3};
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
+ sp<const AATest> a = new AATest{1, emptyMetricId, emptyDimensionId};
+ sp<const AATest> b = new AATest{2, emptyMetricId, emptyDimensionId};
+ sp<const AATest> c = new AATest{3, emptyMetricId, emptyDimensionId};
ipq.push(c);
ipq.push(b);
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d07a84d4c3d8..35e08af68739 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -54,21 +54,26 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
// 2 events in bucket 1.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+
+ // Flushes at event #2.
+ countProducer.flushIfNeeded(bucketStartTimeNs + 2);
+ EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+ // Flushes.
+ countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
- const auto& bucketInfo = buckets[0];
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
- EXPECT_EQ(2LL, bucketInfo.mCount);
+ EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
+ EXPECT_EQ(2LL, buckets[0].mCount);
// 1 matched event happens in bucket 2.
LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ countProducer.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
@@ -79,7 +84,7 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
EXPECT_EQ(1LL, bucketInfo2.mCount);
// nothing happens in bucket 3. we should not record anything for bucket 3.
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
+ countProducer.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
@@ -108,20 +113,22 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
+ // Upon this match event, the matched event1 is flushed.
countProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-
+ countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets.size());
- const auto& bucketInfo = buckets[0];
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
- EXPECT_EQ(1LL, bucketInfo.mCount);
+ {
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ EXPECT_EQ(1UL, buckets.size());
+ const auto& bucketInfo = buckets[0];
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+ EXPECT_EQ(1LL, bucketInfo.mCount);
+ }
}
TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
@@ -138,15 +145,13 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
link->add_key_in_condition()->set_key(2);
LogEvent event1(1, bucketStartTimeNs + 1);
- auto list = event1.GetAndroidLogEventList();
- *list << "111"; // uid
+ event1.write("111"); // uid
event1.init();
ConditionKey key1;
key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
LogEvent event2(1, bucketStartTimeNs + 10);
- auto list2 = event2.GetAndroidLogEventList();
- *list2 << "222"; // uid
+ event2.write("222"); // uid
event2.init();
ConditionKey key2;
key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
@@ -160,10 +165,11 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
-
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ countProducer.flushIfNeeded(bucketStartTimeNs + 1);
+ EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+ countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
@@ -175,6 +181,68 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
EXPECT_EQ(1LL, bucketInfo.mCount);
}
+TEST(CountMetricProducerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(2);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
+
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 30 * NS_PER_SEC;
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+
+ CountMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+ bucketStartTimeNs);
+ countProducer.addAnomalyTracker(anomalyTracker);
+
+ int tagId = 1;
+ LogEvent event1(tagId, bucketStartTimeNs + 1);
+ LogEvent event2(tagId, bucketStartTimeNs + 2);
+ LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1);
+ LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2);
+ LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3);
+ LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3 + NS_PER_SEC);
+
+ // Two events in bucket #0.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+ // One event in bucket #2. No alarm as bucket #0 is trashed out.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+ // Two events in bucket #3.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6, false);
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
+ // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event5.GetTimestampNs());
+
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7, false);
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event7.GetTimestampNs());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 0971d2682d40..18d177cb9173 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -95,15 +95,13 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
link->add_key_in_condition()->set_key(2);
LogEvent event1(1, bucketStartTimeNs + 1);
- auto list = event1.GetAndroidLogEventList();
- *list << "111"; // uid
+ event1.write("111"); // uid
event1.init();
ConditionKey key1;
key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
LogEvent event2(1, bucketStartTimeNs + 10);
- auto list2 = event2.GetAndroidLogEventList();
- *list2 << "222"; // uid
+ event2.write("222"); // uid
event2.init();
ConditionKey key2;
key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
new file mode 100644
index 000000000000..b9e2b8a7752f
--- /dev/null
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -0,0 +1,201 @@
+// 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 "logd/LogEvent.h"
+#include "metrics_test_helper.h"
+#include "src/metrics/GaugeMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(GaugeMetricProducerTest, TestWithCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ GaugeMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_gauge_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ GaugeMetricProducer gaugeProducer(metric, 1 /*has condition*/, wizard, -1, bucketStartTimeNs);
+
+ vector<std::shared_ptr<LogEvent>> allData;
+ std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+ event1->write(1);
+ event1->write(13);
+ event1->init();
+ allData.push_back(event1);
+
+ std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+ allData.push_back(event2);
+
+ gaugeProducer.onDataPulled(allData);
+ gaugeProducer.flushIfNeeded(event2->GetTimestampNs() + 1);
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+
+ gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 11);
+ gaugeProducer.onConditionChanged(false, bucketStartTimeNs + 21);
+ gaugeProducer.onConditionChanged(true, bucketStartTimeNs + bucketSizeNs + 11);
+ std::shared_ptr<LogEvent> event3 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ event3->write(1);
+ event3->write(25);
+ event3->init();
+ allData.push_back(event3);
+ gaugeProducer.onDataPulled(allData);
+ gaugeProducer.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ // One dimension.
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ gaugeProducer.mPastBuckets.begin()->second.front().mBucketStartNs);
+}
+
+TEST(GaugeMetricProducerTest, TestNoCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ GaugeMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_gauge_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+
+ vector<std::shared_ptr<LogEvent>> allData;
+ std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+ event1->write(1);
+ event1->write(13);
+ event1->init();
+ allData.push_back(event1);
+
+ std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+ allData.push_back(event2);
+
+ std::shared_ptr<LogEvent> event3 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ event3->write(1);
+ event3->write(25);
+ event3->init();
+ allData.push_back(event3);
+
+ gaugeProducer.onDataPulled(allData);
+ // Has one slice
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(13L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
+ EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
+ EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mGauge);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ gaugeProducer.mPastBuckets.begin()->second.back().mBucketStartNs);
+}
+
+TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ GaugeMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_gauge_field(2);
+ GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(25);
+ alert.set_number_of_buckets(2);
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ gaugeProducer.addAnomalyTracker(anomalyTracker);
+
+ std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+ event1->write(1);
+ event1->write(13);
+ event1->init();
+
+ gaugeProducer.onDataPulled({event1});
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+ std::shared_ptr<LogEvent> event2 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + bucketSizeNs + 10);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+
+ gaugeProducer.onDataPulled({event2});
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event2->GetTimestampNs());
+
+ std::shared_ptr<LogEvent> event3 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ event3->write(1);
+ event3->write(24);
+ event3->init();
+
+ gaugeProducer.onDataPulled({event3});
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
+
+ // The event4 does not have the gauge field. Thus the current bucket value is 0.
+ std::shared_ptr<LogEvent> event4 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 3 * bucketSizeNs + 10);
+ event4->write(1);
+ event4->init();
+ gaugeProducer.onDataPulled({event4});
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 58bf1b30a9d0..9e169bbf10ef 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -45,17 +45,49 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
- tracker.noteStart("", true, bucketStartTimeNs, key1);
- tracker.noteStop("", bucketStartTimeNs + 10);
+ tracker.noteStart("1", true, bucketStartTimeNs, key1);
+ // Event starts again. This would not change anything as it already starts.
+ tracker.noteStart("1", true, bucketStartTimeNs + 3, key1);
+ // Stopped.
+ tracker.noteStop("1", bucketStartTimeNs + 10, false);
- tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
- tracker.noteStop("", bucketStartTimeNs + 40);
+ // Another event starts in this bucket.
+ tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+ tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(20, buckets[0].mDuration);
+ EXPECT_EQ(20ULL, buckets[0].mDuration);
+}
+
+TEST(MaxDurationTrackerTest, TestStopAll) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucket> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
+
+ // Another event starts in this bucket.
+ tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+ tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
+ EXPECT_TRUE(tracker.mInfos.empty());
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(40ULL, buckets[1].mDuration);
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
@@ -67,14 +99,67 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+ // The event starts.
tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+
+ // Starts again. Does not change anything.
+ tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1);
+
+ // Flushes at early 2nd bucket. The event still does not stop yet.
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+
+ // Flushes at the end of the 2nd bucket. The event still does not stop yet.
+ tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs));
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+ EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
+
+ // The event stops at early 4th bucket.
+ tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/);
+ tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 21);
+ EXPECT_EQ(3u, buckets.size());
+ EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+ EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[2].mDuration);
+}
+
+TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucket> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ // 2 starts
+ tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+ tracker.noteStart("", true, bucketStartTimeNs + 10, key1);
+ // one stop
+ tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
+
tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
EXPECT_EQ(2u, buckets.size());
- EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration);
- EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+
+ // real stop now.
+ tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+ tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
+
+ EXPECT_EQ(3u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ(5ULL, buckets[2].mDuration);
}
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
@@ -93,17 +178,65 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t durationTimeNs = 2 * 1000;
- MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+ EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
- tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+ EXPECT_TRUE(tracker.mInfos.empty());
+ EXPECT_EQ(6LL, tracker.mDuration);
+
+ tracker.noteStart("2:maps", false, eventStartTimeNs + 3 * bucketSizeNs + 10, key1);
+ EXPECT_EQ(1u, tracker.mInfos.size());
+ for (const auto& itr : tracker.mInfos) {
+ EXPECT_EQ(DurationState::kPaused, itr.second.state);
+ EXPECT_EQ(0LL, itr.second.lastDuration);
+ }
+ EXPECT_EQ(3u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ(6ULL, buckets[2].mDuration);
+}
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(32 * NS_PER_SEC);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
- EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(5, buckets[0].mDuration);
+ vector<DurationBucket> buckets;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+ uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs,
+ {anomalyTracker}, buckets);
+
+ tracker.noteStart("1", true, eventStartTimeNs, key1);
+ tracker.noteStop("1", eventStartTimeNs + 10, false);
+ EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
+ EXPECT_EQ(10LL, tracker.mDuration);
+
+ tracker.noteStart("2", true, eventStartTimeNs + 20, key1);
+ tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
+ EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
+ EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs,
+ (long long)(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC));
}
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 74a6f11754f3..f4edffd1af1e 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -45,18 +45,105 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) {
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t durationTimeNs = 2 * 1000;
+ uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
+ EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+ tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestDurationNested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ OringDurationTracker tracker("event", wizard, 1, true, 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 + 2000, false);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
+ EXPECT_EQ(2003ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestStopAll) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
+
+ tracker.noteStopAll(eventStartTimeNs + 2003);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(2003ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t durationTimeNs = 2 * 1000;
+
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1);
+ EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
+
+ tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false);
+ tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
}
TEST(OringDurationTrackerTest, TestDurationConditionChange) {
@@ -73,20 +160,151 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) {
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t durationTimeNs = 2 * 1000;
+ uint64_t durationTimeNs = 2 * 1000;
+
+ OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
+ tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + durationTimeNs);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ EXPECT_CALL(*wizard, query(_, key1)) // #4
+ .WillOnce(Return(ConditionState::kFalse));
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
- tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+ tracker.noteStop("2:maps", eventStartTimeNs + 3, false);
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(5, buckets[0].mDuration);
+ EXPECT_EQ(15ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
+
+ vector<DurationBucket> buckets;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+ uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs,
+ {anomalyTracker}, buckets);
+
+ // Nothing in the past bucket.
+ tracker.noteStart("", true, eventStartTimeNs, key1);
+ EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
+
+ tracker.noteStop("", eventStartTimeNs + 3, false);
+ EXPECT_EQ(0u, buckets.size());
+
+ uint64_t event1StartTimeNs = eventStartTimeNs + 10;
+ tracker.noteStart("1", true, event1StartTimeNs, key1);
+ // No past buckets. The anomaly will happen in bucket #0.
+ EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
+
+ uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
+ tracker.noteStop("1", event1StopTimeNs, false);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
+ buckets[0].mDuration);
+
+ const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
+ const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
+
+ // One past buckets. The anomaly will happen in bucket #1.
+ uint64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
+ tracker.noteStart("1", true, event2StartTimeNs, key1);
+ EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
+ bucket1Duration),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
+ tracker.noteStop("1", event2StartTimeNs + 1, false);
+
+ // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
+ // bucket #2.
+ uint64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
+ tracker.noteStart("1", true, event3StartTimeNs, key1);
+ EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
}
+
+TEST(OringDurationTrackerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
+
+ vector<DurationBucket> buckets;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+ uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ OringDurationTracker tracker("event", wizard, 1, true /*nesting*/, bucketStartTimeNs,
+ bucketSizeNs, {anomalyTracker}, buckets);
+
+ tracker.noteStart("", true, eventStartTimeNs, key1);
+ tracker.noteStop("", eventStartTimeNs + 10, false);
+ EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
+ EXPECT_TRUE(tracker.mStarted.empty());
+ EXPECT_EQ(-1LL, tracker.mLastStartTime);
+ EXPECT_EQ(10LL, tracker.mDuration);
+
+ EXPECT_EQ(0u, tracker.mStarted.size());
+
+ tracker.noteStart("", true, eventStartTimeNs + 20, key1);
+ EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+ EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
+ (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
+ tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false);
+ EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs));
+ EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
+ anomalyTracker->mLastAlarmTimestampNs);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 72b4194d533e..1ed3636f4ec4 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -64,9 +64,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
vector<shared_ptr<LogEvent>> allData;
allData.clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- auto list = event->GetAndroidLogEventList();
- *list << 1;
- *list << 11;
+ event->write(1);
+ event->write(11);
event->init();
allData.push_back(event);
@@ -89,9 +88,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
allData.clear();
event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- list = event->GetAndroidLogEventList();
- *list << 1;
- *list << 22;
+ event->write(1);
+ event->write(22);
event->init();
allData.push_back(event);
valueProducer.onDataPulled(allData);
@@ -110,9 +108,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
allData.clear();
event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- list = event->GetAndroidLogEventList();
- *list << 1;
- *list << 33;
+ event->write(1);
+ event->write(33);
event->init();
allData.push_back(event);
valueProducer.onDataPulled(allData);
@@ -159,9 +156,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- auto list = event->GetAndroidLogEventList();
- *list << 1;
- *list << 100;
+ event->write(1);
+ event->write(100);
event->init();
data->push_back(event);
return true;
@@ -174,9 +170,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
- auto list = event->GetAndroidLogEventList();
- *list << 1;
- *list << 120;
+ event->write(1);
+ event->write(120);
event->init();
data->push_back(event);
return true;
@@ -201,9 +196,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
vector<shared_ptr<LogEvent>> allData;
allData.clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- auto list = event->GetAndroidLogEventList();
- *list << 1;
- *list << 110;
+ event->write(1);
+ event->write(110);
event->init();
allData.push_back(event);
valueProducer.onDataPulled(allData);
@@ -253,14 +247,12 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
bucketStartTimeNs, pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- auto list = event1->GetAndroidLogEventList();
- *list << 1;
- *list << 10;
+ event1->write(1);
+ event1->write(10);
event1->init();
shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- auto list2 = event2->GetAndroidLogEventList();
- *list2 << 1;
- *list2 << 20;
+ event2->write(1);
+ event2->write(20);
event2->init();
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1, false);
// has one slice
@@ -285,7 +277,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
EXPECT_EQ(20, curInterval.raw.back().first);
EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
- valueProducer.flush_if_needed(bucket3StartTimeNs);
+ valueProducer.flushIfNeeded(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
index 66e762c0558b..955e58ed933b 100644
--- a/cmds/webview_zygote/Android.mk
+++ b/cmds/webview_zygote/Android.mk
@@ -21,6 +21,8 @@ LOCAL_MODULE := webview_zygote
LOCAL_SRC_FILES := webview_zygote.cpp
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libbinder \
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 99f3dee7285d..03a3631b111f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4379,7 +4379,7 @@ public class Activity extends ContextThemeWrapper
throw new IllegalArgumentException("requestCode should be >= 0");
}
if (mHasCurrentPermissionsRequest) {
- Log.w(TAG, "Can reqeust only one set of permissions at a time");
+ Log.w(TAG, "Can request only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 21e454f11ebc..95c5fb53a014 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5380,7 +5380,7 @@ public final class ActivityThread {
}
}
- GraphicsEnvironment.chooseDriver(context);
+ GraphicsEnvironment.getInstance().setup(context);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index b7c1f4e082e2..725704422290 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -17,9 +17,12 @@
package android.app;
import android.os.Build;
+import android.os.GraphicsEnvironment;
import android.os.Trace;
import android.util.ArrayMap;
+
import com.android.internal.os.ClassLoaderFactory;
+
import dalvik.system.PathClassLoader;
/** @hide */
@@ -72,8 +75,9 @@ public class ApplicationLoaders {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
- setupVulkanLayerPath(classloader, librarySearchPath);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths");
+ GraphicsEnvironment.getInstance().setLayerPaths(
+ classloader, librarySearchPath, libraryPermittedPath);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
mLoaders.put(cacheKey, classloader);
@@ -105,8 +109,6 @@ public class ApplicationLoaders {
cacheKey, null /* classLoaderName */);
}
- private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
-
/**
* Adds a new path the classpath of the given loader.
* @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0eafdec6bb0f..7a4c00fcc5af 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -35,7 +35,6 @@ import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
@@ -1681,21 +1680,8 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
- public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName) {
- installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, mContext.getUserId());
- }
-
- @Override
- public void installPackage(Uri packageURI, PackageInstallObserver observer,
- int flags, String installerPackageName) {
- installCommon(packageURI, observer, flags, installerPackageName, mContext.getUserId());
- }
-
- private void installCommon(Uri packageURI,
- PackageInstallObserver observer, int flags, String installerPackageName,
- int userId) {
+ public void installPackage(Uri packageURI,
+ PackageInstallObserver observer, int flags, String installerPackageName) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
@@ -1703,7 +1689,7 @@ public class ApplicationPackageManager extends PackageManager {
final String originPath = packageURI.getPath();
try {
mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
- userId);
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d5d95fb85a6a..42c1347e265d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -68,6 +68,7 @@ import android.text.style.TextAppearanceSpan;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -2447,6 +2448,30 @@ public class Notification implements Parcelable
notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
}
+ /**
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(NotificationProto.CHANNEL_ID, getChannelId());
+ proto.write(NotificationProto.HAS_TICKER_TEXT, this.tickerText != null);
+ proto.write(NotificationProto.FLAGS, this.flags);
+ proto.write(NotificationProto.COLOR, this.color);
+ proto.write(NotificationProto.CATEGORY, this.category);
+ proto.write(NotificationProto.GROUP_KEY, this.mGroupKey);
+ proto.write(NotificationProto.SORT_KEY, this.mSortKey);
+ if (this.actions != null) {
+ proto.write(NotificationProto.ACTION_LENGTH, this.actions.length);
+ }
+ if (this.visibility >= VISIBILITY_SECRET && this.visibility <= VISIBILITY_PUBLIC) {
+ proto.write(NotificationProto.VISIBILITY, this.visibility);
+ }
+ if (publicVersion != null) {
+ publicVersion.writeToProto(proto, NotificationProto.PUBLIC_VERSION);
+ }
+ proto.end(token);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index e491a4f901e8..da5569d27c73 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -359,6 +359,8 @@ public class AssistStructure implements Parcelable {
if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
+ ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
+ ", views=" + mNumReadViews);
+ mCurParcel.recycle();
+ mCurParcel = null; // Parcel cannot be used after recycled.
}
Parcel readParcel(int validateToken, int level) {
@@ -396,20 +398,23 @@ public class AssistStructure implements Parcelable {
private void fetchData() {
Parcel data = Parcel.obtain();
- data.writeInterfaceToken(DESCRIPTOR);
- data.writeStrongBinder(mTransferToken);
- if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
- if (mCurParcel != null) {
- mCurParcel.recycle();
- }
- mCurParcel = Parcel.obtain();
try {
- mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failure reading AssistStructure data", e);
- throw new IllegalStateException("Failure reading AssistStructure data: " + e);
+ data.writeInterfaceToken(DESCRIPTOR);
+ data.writeStrongBinder(mTransferToken);
+ if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
+ if (mCurParcel != null) {
+ mCurParcel.recycle();
+ }
+ mCurParcel = Parcel.obtain();
+ try {
+ mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failure reading AssistStructure data", e);
+ throw new IllegalStateException("Failure reading AssistStructure data: " + e);
+ }
+ } finally {
+ data.recycle();
}
- data.recycle();
mNumReadWindows = mNumReadViews = 0;
}
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 616a5be3e552..58fb260814e5 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -62,7 +62,6 @@ public final class Slice implements Parcelable {
* the content should be used in the shortcut representation of the slice (icon, label, action),
* normally this should be indicated by adding the hint on the action containing that content.
*
- * @see SliceView#MODE_SHORTCUT
* @see SliceItem#TYPE_ACTION
*/
public static final String HINT_TITLE = "title";
@@ -109,9 +108,9 @@ public final class Slice implements Parcelable {
*/
public static final String HINT_NO_TINT = "no_tint";
/**
- * Hint to indicate that this content should not be shown in the {@link SliceView#MODE_SMALL}
- * and {@link SliceView#MODE_LARGE} modes of SliceView. This content may be used to populate
- * the {@link SliceView#MODE_SHORTCUT} format of the slice.
+ * Hint to indicate that this content should not be shown in larger renderings
+ * of Slices. This content may be used to populate the shortcut/icon
+ * format of the slice.
* @hide
*/
public static final String HINT_HIDDEN = "hidden";
@@ -144,12 +143,14 @@ public final class Slice implements Parcelable {
private final SliceItem[] mItems;
private final @SliceHint String[] mHints;
+ private SliceSpec mSpec;
private Uri mUri;
- Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
+ Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri, SliceSpec spec) {
mHints = hints;
mItems = items.toArray(new SliceItem[items.size()]);
mUri = uri;
+ mSpec = spec;
}
protected Slice(Parcel in) {
@@ -160,6 +161,14 @@ public final class Slice implements Parcelable {
mItems[i] = SliceItem.CREATOR.createFromParcel(in);
}
mUri = Uri.CREATOR.createFromParcel(in);
+ mSpec = in.readTypedObject(SliceSpec.CREATOR);
+ }
+
+ /**
+ * @return The spec for this slice
+ */
+ public @Nullable SliceSpec getSpec() {
+ return mSpec;
}
/**
@@ -191,6 +200,7 @@ public final class Slice implements Parcelable {
mItems[i].writeToParcel(dest, flags);
}
mUri.writeToParcel(dest, 0);
+ dest.writeTypedObject(mSpec, flags);
}
@Override
@@ -213,6 +223,7 @@ public final class Slice implements Parcelable {
private final Uri mUri;
private ArrayList<SliceItem> mItems = new ArrayList<>();
private @SliceHint ArrayList<String> mHints = new ArrayList<>();
+ private SliceSpec mSpec;
/**
* Create a builder which will construct a {@link Slice} for the Given Uri.
@@ -248,11 +259,21 @@ public final class Slice implements Parcelable {
}
/**
+ * Add the spec for this slice.
+ */
+ public Builder setSpec(SliceSpec spec) {
+ mSpec = spec;
+ return this;
+ }
+
+ /**
* Add a sub-slice to the slice being constructed
*/
public Builder addSubSlice(@NonNull Slice slice) {
- mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints().toArray(
- new String[slice.getHints().size()])));
+ List<String> hints = slice.getHints();
+ slice.mSpec = null;
+ mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, hints.toArray(
+ new String[hints.size()])));
return this;
}
@@ -260,7 +281,10 @@ public final class Slice implements Parcelable {
* Add an action to the slice being constructed
*/
public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
- mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, new String[0]));
+ List<String> hints = s.getHints();
+ s.mSpec = null;
+ mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, hints.toArray(
+ new String[hints.size()])));
return this;
}
@@ -352,7 +376,7 @@ public final class Slice implements Parcelable {
* Construct the slice.
*/
public Slice build() {
- return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri);
+ return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri, mSpec);
}
}
@@ -400,10 +424,12 @@ public final class Slice implements Parcelable {
*
* @param resolver ContentResolver to be used.
* @param uri The URI to a slice provider
+ * @param supportedSpecs List of supported specs.
* @return The Slice provided by the app or null if none is given.
* @see Slice
*/
- public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri) {
+ public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
+ List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = resolver.acquireProvider(uri);
if (provider == null) {
@@ -412,6 +438,8 @@ public final class Slice implements Parcelable {
try {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
null, extras);
Bundle.setDefusable(res, true);
@@ -435,12 +463,14 @@ public final class Slice implements Parcelable {
*
* @param context The context to use.
* @param intent The intent associated with a slice.
+ * @param supportedSpecs List of supported specs.
* @return The Slice provided by the app or null if none is given.
* @see Slice
* @see SliceProvider#onMapIntentToUri(Intent)
* @see Intent
*/
- public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent) {
+ public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
+ List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(intent, "intent");
Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
"Slice intent must be explicit " + intent);
@@ -449,7 +479,7 @@ public final class Slice implements Parcelable {
// Check if the intent has data for the slice uri on it and use that
final Uri intentData = intent.getData();
if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return bindSlice(resolver, intentData);
+ return bindSlice(resolver, intentData, supportedSpecs);
}
// Otherwise ask the app
List<ResolveInfo> providers =
@@ -467,6 +497,8 @@ public final class Slice implements Parcelable {
try {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(resolver.getPackageName(),
SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 05f4ce6eb10f..ac5365c35f49 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -17,7 +17,6 @@ package android.app.slice;
import android.Manifest.permission;
import android.annotation.NonNull;
-import android.app.slice.widget.SliceView;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -37,6 +36,7 @@ import android.os.StrictMode.ThreadPolicy;
import android.os.UserHandle;
import android.util.Log;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -93,6 +93,10 @@ public abstract class SliceProvider extends ContentProvider {
/**
* @hide
*/
+ public static final String EXTRA_SUPPORTED_SPECS = "supported_specs";
+ /**
+ * @hide
+ */
public static final String METHOD_SLICE = "bind_slice";
/**
* @hide
@@ -118,12 +122,25 @@ public abstract class SliceProvider extends ContentProvider {
* off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
* when the app is ready to provide the complete data in onBindSlice.
* <p>
+ * The slice returned should have a spec that is compatible with one of
+ * the supported specs.
*
+ * @param sliceUri Uri to bind.
+ * @param supportedSpecs List of supported specs.
* @see {@link Slice}.
* @see {@link Slice#HINT_PARTIAL}
*/
- // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)).
- public abstract Slice onBindSlice(Uri sliceUri);
+ public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+ return onBindSlice(sliceUri);
+ }
+
+ /**
+ * @deprecated migrating to {@link #onBindSlice(Uri, List)}
+ */
+ @Deprecated
+ public Slice onBindSlice(Uri sliceUri) {
+ return null;
+ }
/**
* This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
@@ -132,7 +149,6 @@ public abstract class SliceProvider extends ContentProvider {
*
* @return Uri representing the slice associated with the provided intent.
* @see {@link Slice}
- * @see {@link SliceView#setSlice(Intent)}
*/
public @NonNull Uri onMapIntentToUri(Intent intent) {
throw new UnsupportedOperationException(
@@ -195,8 +211,9 @@ public abstract class SliceProvider extends ContentProvider {
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
"Slice binding requires the permission BIND_SLICE");
}
+ List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
- Slice s = handleBindSlice(uri);
+ Slice s = handleBindSlice(uri, supportedSpecs);
Bundle b = new Bundle();
b.putParcelable(EXTRA_SLICE, s);
return b;
@@ -205,9 +222,10 @@ public abstract class SliceProvider extends ContentProvider {
"Slice binding requires the permission BIND_SLICE");
Intent intent = extras.getParcelable(EXTRA_INTENT);
Uri uri = onMapIntentToUri(intent);
+ List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Bundle b = new Bundle();
if (uri != null) {
- Slice s = handleBindSlice(uri);
+ Slice s = handleBindSlice(uri, supportedSpecs);
b.putParcelable(EXTRA_SLICE, s);
} else {
b.putParcelable(EXTRA_SLICE, null);
@@ -217,14 +235,14 @@ public abstract class SliceProvider extends ContentProvider {
return super.call(method, arg, extras);
}
- private Slice handleBindSlice(Uri sliceUri) {
+ private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
if (Looper.myLooper() == Looper.getMainLooper()) {
- return onBindSliceStrict(sliceUri);
+ return onBindSliceStrict(sliceUri, supportedSpecs);
} else {
CountDownLatch latch = new CountDownLatch(1);
Slice[] output = new Slice[1];
Handler.getMain().post(() -> {
- output[0] = onBindSliceStrict(sliceUri);
+ output[0] = onBindSliceStrict(sliceUri, supportedSpecs);
latch.countDown();
});
try {
@@ -236,14 +254,14 @@ public abstract class SliceProvider extends ContentProvider {
}
}
- private Slice onBindSliceStrict(Uri sliceUri) {
+ private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDeath()
.build());
- return onBindSlice(sliceUri);
+ return onBindSlice(sliceUri, supportedSpecs);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
diff --git a/core/java/android/app/slice/SliceSpec.java b/core/java/android/app/slice/SliceSpec.java
new file mode 100644
index 000000000000..433b67e9aacb
--- /dev/null
+++ b/core/java/android/app/slice/SliceSpec.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.slice;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class describing the structure of the data contained within a slice.
+ * <p>
+ * A data version contains a string which describes the type of structure
+ * and a revision which denotes this specific implementation. Revisions are expected
+ * to be backwards compatible and monotonically increasing. Meaning if a
+ * SliceSpec has the same type and an equal or lesser revision,
+ * it is expected to be compatible.
+ * <p>
+ * Apps rendering slices will provide a list of supported versions to the OS which
+ * will also be given to the app. Apps should only return a {@link Slice} with a
+ * {@link SliceSpec} that one of the supported {@link SliceSpec}s provided
+ * {@link #canRender}.
+ *
+ * @see Slice
+ * @see SliceProvider#onBindSlice(Uri)
+ */
+public final class SliceSpec implements Parcelable {
+
+ private final String mType;
+ private final int mRevision;
+
+ public SliceSpec(@NonNull String type, int revision) {
+ mType = type;
+ mRevision = revision;
+ }
+
+ /**
+ * @hide
+ */
+ public SliceSpec(Parcel source) {
+ mType = source.readString();
+ mRevision = source.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mType);
+ dest.writeInt(mRevision);
+ }
+
+ /**
+ * Gets the type of the version.
+ */
+ public String getType() {
+ return mType;
+ }
+
+ /**
+ * Gets the revision of the version.
+ */
+ public int getRevision() {
+ return mRevision;
+ }
+
+ /**
+ * Indicates that this spec can be used to render the specified spec.
+ * <p>
+ * Rendering support is not bi-directional (e.g. Spec v3 can render
+ * Spec v2, but Spec v2 cannot render Spec v3).
+ *
+ * @param candidate candidate format of data.
+ * @return true if versions are compatible.
+ * @see androidx.app.slice.widget.SliceView
+ */
+ public boolean canRender(@NonNull SliceSpec candidate) {
+ if (!mType.equals(candidate.mType)) return false;
+ return mRevision >= candidate.mRevision;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SliceSpec)) return false;
+ SliceSpec other = (SliceSpec) obj;
+ return mType.equals(other.mType) && mRevision == other.mRevision;
+ }
+
+ public static final Creator<SliceSpec> CREATOR = new Creator<SliceSpec>() {
+ @Override
+ public SliceSpec createFromParcel(Parcel source) {
+ return new SliceSpec(source);
+ }
+
+ @Override
+ public SliceSpec[] newArray(int size) {
+ return new SliceSpec[size];
+ }
+ };
+}
diff --git a/core/java/android/app/slice/widget/ActionRow.java b/core/java/android/app/slice/widget/ActionRow.java
deleted file mode 100644
index c96e6304f824..000000000000
--- a/core/java/android/app/slice/widget/ActionRow.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteInput;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class ActionRow extends FrameLayout {
-
- private static final int MAX_ACTIONS = 5;
- private final int mSize;
- private final int mIconPadding;
- private final LinearLayout mActionsGroup;
- private final boolean mFullActions;
- private int mColor = Color.BLACK;
-
- public ActionRow(Context context, boolean fullActions) {
- super(context);
- mFullActions = fullActions;
- mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
- context.getResources().getDisplayMetrics());
- mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
- context.getResources().getDisplayMetrics());
- mActionsGroup = new LinearLayout(context);
- mActionsGroup.setOrientation(LinearLayout.HORIZONTAL);
- mActionsGroup.setLayoutParams(
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- addView(mActionsGroup);
- }
-
- private void setColor(int color) {
- mColor = color;
- for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
- View view = mActionsGroup.getChildAt(i);
- SliceItem item = (SliceItem) view.getTag();
- boolean tint = !item.hasHint(Slice.HINT_NO_TINT);
- if (tint) {
- ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
- }
- }
- }
-
- private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) {
- ImageView imageView = new ImageView(getContext());
- imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
- imageView.setScaleType(ScaleType.FIT_CENTER);
- imageView.setImageIcon(icon);
- if (allowTint) {
- imageView.setImageTintList(ColorStateList.valueOf(mColor));
- }
- imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
- android.R.attr.selectableItemBackground));
- imageView.setTag(image);
- addAction(imageView);
- return imageView;
- }
-
- /**
- * Set the actions and color for this action row.
- */
- public void setActions(SliceItem actionRow, SliceItem defColor) {
- removeAllViews();
- mActionsGroup.removeAllViews();
- addView(mActionsGroup);
-
- SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR);
- if (color == null) {
- color = defColor;
- }
- if (color != null) {
- setColor(color.getColor());
- }
- SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> {
- if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
- return;
- }
- SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE);
- if (image == null) {
- return;
- }
- boolean tint = !image.hasHint(Slice.HINT_NO_TINT);
- SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT);
- if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
- addAction(image.getIcon(), tint, image).setOnClickListener(
- v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput()));
- createRemoteInputView(mColor, getContext());
- } else {
- addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute(
- () -> {
- try {
- action.getAction().send();
- } catch (CanceledException e) {
- e.printStackTrace();
- }
- }));
- }
- });
- setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE);
- }
-
- private void addAction(View child) {
- mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
- }
-
- private void createRemoteInputView(int color, Context context) {
- View riv = RemoteInputView.inflate(context, this);
- riv.setVisibility(View.INVISIBLE);
- addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- riv.setBackgroundColor(color);
- }
-
- private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
- RemoteInput input) {
- if (input == null) {
- return false;
- }
-
- ViewParent p = view.getParent().getParent();
- RemoteInputView riv = null;
- while (p != null) {
- if (p instanceof View) {
- View pv = (View) p;
- riv = findRemoteInputView(pv);
- if (riv != null) {
- break;
- }
- }
- p = p.getParent();
- }
- if (riv == null) {
- return false;
- }
-
- int width = view.getWidth();
- if (view instanceof TextView) {
- // Center the reveal on the text which might be off-center from the TextView
- TextView tv = (TextView) view;
- if (tv.getLayout() != null) {
- int innerWidth = (int) tv.getLayout().getLineWidth(0);
- innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
- width = Math.min(width, innerWidth);
- }
- }
- int cx = view.getLeft() + width / 2;
- int cy = view.getTop() + view.getHeight() / 2;
- int w = riv.getWidth();
- int h = riv.getHeight();
- int r = Math.max(
- Math.max(cx + cy, cx + (h - cy)),
- Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
- riv.setRevealParameters(cx, cy, r);
- riv.setPendingIntent(pendingIntent);
- riv.setRemoteInput(new RemoteInput[] {
- input
- }, input);
- riv.focusAnimated();
- return true;
- }
-
- private RemoteInputView findRemoteInputView(View v) {
- if (v == null) {
- return null;
- }
- return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
- }
-}
diff --git a/core/java/android/app/slice/widget/GridView.java b/core/java/android/app/slice/widget/GridView.java
deleted file mode 100644
index 793abc05a489..000000000000
--- a/core/java/android/app/slice/widget/GridView.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class GridView extends LinearLayout implements SliceListView {
-
- private static final String TAG = "GridView";
-
- private static final int MAX_IMAGES = 3;
- private static final int MAX_ALL = 5;
- private boolean mIsAllImages;
-
- public GridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mIsAllImages) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = width / getChildCount();
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
- height);
- getLayoutParams().height = height;
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).getLayoutParams().height = height;
- }
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- public void setSliceItem(SliceItem slice) {
- mIsAllImages = true;
- removeAllViews();
- int total = 1;
- if (slice.getType() == SliceItem.TYPE_SLICE) {
- List<SliceItem> items = slice.getSlice().getItems();
- total = items.size();
- for (int i = 0; i < total; i++) {
- SliceItem item = items.get(i);
- if (isFull()) {
- continue;
- }
- if (!addItem(item)) {
- mIsAllImages = false;
- }
- }
- } else {
- if (!isFull()) {
- if (!addItem(slice)) {
- mIsAllImages = false;
- }
- }
- }
- if (total > getChildCount() && mIsAllImages) {
- addExtraCount(total - getChildCount());
- }
- }
-
- private void addExtraCount(int numExtra) {
- View last = getChildAt(getChildCount() - 1);
- FrameLayout frame = new FrameLayout(getContext());
- frame.setLayoutParams(last.getLayoutParams());
-
- removeView(last);
- frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
- TextView v = new TextView(getContext());
- v.setTextColor(Color.WHITE);
- v.setBackgroundColor(0x4d000000);
- v.setText(getResources().getString(R.string.slice_more_content, numExtra));
- v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
- v.setGravity(Gravity.CENTER);
- frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
- addView(frame);
- }
-
- private boolean isFull() {
- return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL);
- }
-
- /**
- * Returns true if this item is just an image.
- */
- private boolean addItem(SliceItem item) {
- if (item.hasHint(Slice.HINT_HIDDEN)) {
- return false;
- }
- if (item.getType() == SliceItem.TYPE_IMAGE) {
- ImageView v = new ImageView(getContext());
- v.setImageIcon(item.getIcon());
- v.setScaleType(ScaleType.CENTER_CROP);
- addView(v, new LayoutParams(0, MATCH_PARENT, 1));
- return true;
- } else {
- LinearLayout v = new LinearLayout(getContext());
- int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 12, getContext().getResources().getDisplayMetrics());
- v.setPadding(0, s, 0, 0);
- v.setOrientation(LinearLayout.VERTICAL);
- v.setGravity(Gravity.CENTER_HORIZONTAL);
- // TODO: Unify sporadic inflates that happen throughout the code.
- ArrayList<SliceItem> items = new ArrayList<>();
- if (item.getType() == SliceItem.TYPE_SLICE) {
- items.addAll(item.getSlice().getItems());
- }
- items.forEach(i -> {
- if (i.hasHint(Slice.HINT_HIDDEN)) {
- return;
- }
- Context context = getContext();
- switch (i.getType()) {
- case SliceItem.TYPE_TEXT:
- boolean title = false;
- if ((item.hasAnyHints(new String[] {
- Slice.HINT_LARGE, Slice.HINT_TITLE
- }))) {
- title = true;
- }
- TextView tv = (TextView) LayoutInflater.from(context).inflate(
- title ? R.layout.slice_title : R.layout.slice_secondary_text, null);
- tv.setText(i.getText());
- v.addView(tv);
- break;
- case SliceItem.TYPE_IMAGE:
- ImageView iv = new ImageView(context);
- iv.setImageIcon(i.getIcon());
- if (item.hasHint(Slice.HINT_LARGE)) {
- iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- } else {
- int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 48, context.getResources().getDisplayMetrics());
- iv.setLayoutParams(new LayoutParams(size, size));
- }
- v.addView(iv);
- break;
- case SliceItem.TYPE_REMOTE_VIEW:
- v.addView(i.getRemoteView().apply(context, v));
- break;
- case SliceItem.TYPE_COLOR:
- // TODO: Support color to tint stuff here.
- break;
- }
- });
- addView(v, new LayoutParams(0, WRAP_CONTENT, 1));
- return false;
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/LargeSliceAdapter.java b/core/java/android/app/slice/widget/LargeSliceAdapter.java
deleted file mode 100644
index 267fff6a4572..000000000000
--- a/core/java/android/app/slice/widget/LargeSliceAdapter.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceViewHolder;
-import android.content.Context;
-import android.util.ArrayMap;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.widget.RecyclerView;
-import com.android.internal.widget.RecyclerView.ViewHolder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * @hide
- */
-public class LargeSliceAdapter extends RecyclerView.Adapter<SliceViewHolder> {
-
- public static final int TYPE_DEFAULT = 1;
- public static final int TYPE_HEADER = 2;
- public static final int TYPE_GRID = 3;
- public static final int TYPE_MESSAGE = 4;
- public static final int TYPE_MESSAGE_LOCAL = 5;
- public static final int TYPE_REMOTE_VIEWS = 6;
-
- private final IdGenerator mIdGen = new IdGenerator();
- private final Context mContext;
- private List<SliceWrapper> mSlices = new ArrayList<>();
- private SliceItem mColor;
-
- public LargeSliceAdapter(Context context) {
- mContext = context;
- setHasStableIds(true);
- }
-
- /**
- * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
- */
- public void setSliceItems(List<SliceItem> slices, SliceItem color) {
- mColor = color;
- mIdGen.resetUsage();
- mSlices = slices.stream().map(s -> new SliceWrapper(s, mIdGen))
- .collect(Collectors.toList());
- notifyDataSetChanged();
- }
-
- @Override
- public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View v = inflateForType(viewType);
- v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- return new SliceViewHolder(v);
- }
-
- @Override
- public int getItemViewType(int position) {
- return mSlices.get(position).mType;
- }
-
- @Override
- public long getItemId(int position) {
- return mSlices.get(position).mId;
- }
-
- @Override
- public int getItemCount() {
- return mSlices.size();
- }
-
- @Override
- public void onBindViewHolder(SliceViewHolder holder, int position) {
- SliceWrapper slice = mSlices.get(position);
- if (holder.mSliceView != null) {
- holder.mSliceView.setColor(mColor);
- holder.mSliceView.setSliceItem(slice.mItem);
- } else if (slice.mType == TYPE_REMOTE_VIEWS) {
- FrameLayout frame = (FrameLayout) holder.itemView;
- frame.removeAllViews();
- frame.addView(slice.mItem.getRemoteView().apply(mContext, frame));
- }
- }
-
- private View inflateForType(int viewType) {
- switch (viewType) {
- case TYPE_REMOTE_VIEWS:
- return new FrameLayout(mContext);
- case TYPE_GRID:
- return LayoutInflater.from(mContext).inflate(R.layout.slice_grid, null);
- case TYPE_MESSAGE:
- return LayoutInflater.from(mContext).inflate(R.layout.slice_message, null);
- case TYPE_MESSAGE_LOCAL:
- return LayoutInflater.from(mContext).inflate(R.layout.slice_message_local, null);
- }
- return new SmallTemplateView(mContext);
- }
-
- protected static class SliceWrapper {
- private final SliceItem mItem;
- private final int mType;
- private final long mId;
-
- public SliceWrapper(SliceItem item, IdGenerator idGen) {
- mItem = item;
- mType = getType(item);
- mId = idGen.getId(item);
- }
-
- public static int getType(SliceItem item) {
- if (item.getType() == SliceItem.TYPE_REMOTE_VIEW) {
- return TYPE_REMOTE_VIEWS;
- }
- if (item.hasHint(Slice.HINT_MESSAGE)) {
- // TODO: Better way to determine me or not? Something more like Messaging style.
- if (SliceQuery.find(item, -1, Slice.HINT_SOURCE, null) != null) {
- return TYPE_MESSAGE;
- } else {
- return TYPE_MESSAGE_LOCAL;
- }
- }
- if (item.hasHint(Slice.HINT_HORIZONTAL)) {
- return TYPE_GRID;
- }
- return TYPE_DEFAULT;
- }
- }
-
- /**
- * A {@link ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
- */
- public static class SliceViewHolder extends ViewHolder {
- public final SliceListView mSliceView;
-
- public SliceViewHolder(View itemView) {
- super(itemView);
- mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null;
- }
- }
-
- /**
- * View slices being displayed in {@link LargeSliceAdapter}.
- */
- public interface SliceListView {
- /**
- * Set the slice item for this view.
- */
- void setSliceItem(SliceItem slice);
-
- /**
- * Set the color for the items in this view.
- */
- default void setColor(SliceItem color) {
-
- }
- }
-
- private static class IdGenerator {
- private long mNextLong = 0;
- private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
- private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
-
- public long getId(SliceItem item) {
- String str = genString(item);
- if (!mCurrentIds.containsKey(str)) {
- mCurrentIds.put(str, mNextLong++);
- }
- long id = mCurrentIds.get(str);
- int index = mUsedIds.getOrDefault(str, 0);
- mUsedIds.put(str, index + 1);
- return id + index * 10000;
- }
-
- private String genString(SliceItem item) {
- StringBuilder builder = new StringBuilder();
- SliceQuery.stream(item).forEach(i -> {
- builder.append(i.getType());
- i.removeHint(Slice.HINT_SELECTED);
- builder.append(i.getHints());
- switch (i.getType()) {
- case SliceItem.TYPE_REMOTE_VIEW:
- builder.append(i.getRemoteView());
- break;
- case SliceItem.TYPE_IMAGE:
- builder.append(i.getIcon());
- break;
- case SliceItem.TYPE_TEXT:
- builder.append(i.getText());
- break;
- case SliceItem.TYPE_COLOR:
- builder.append(i.getColor());
- break;
- }
- });
- return builder.toString();
- }
-
- public void resetUsage() {
- mUsedIds.clear();
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/LargeTemplateView.java b/core/java/android/app/slice/widget/LargeTemplateView.java
deleted file mode 100644
index 788f6fb6d10a..000000000000
--- a/core/java/android/app/slice/widget/LargeTemplateView.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.util.TypedValue;
-
-import com.android.internal.widget.LinearLayoutManager;
-import com.android.internal.widget.RecyclerView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class LargeTemplateView extends SliceModeView {
-
- private final LargeSliceAdapter mAdapter;
- private final RecyclerView mRecyclerView;
- private final int mDefaultHeight;
- private final int mMaxHeight;
- private Slice mSlice;
- private boolean mIsScrollable;
-
- public LargeTemplateView(Context context) {
- super(context);
-
- mRecyclerView = new RecyclerView(getContext());
- mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- mAdapter = new LargeSliceAdapter(context);
- mRecyclerView.setAdapter(mAdapter);
- addView(mRecyclerView);
- mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
- getResources().getDisplayMetrics());
- mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
- getResources().getDisplayMetrics());
- }
-
- @Override
- public String getMode() {
- return SliceView.MODE_LARGE;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mRecyclerView.getMeasuredHeight() > mMaxHeight
- || (mSlice != null && mSlice.hasHint(Slice.HINT_PARTIAL))) {
- mRecyclerView.getLayoutParams().height = mDefaultHeight;
- } else {
- mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- public void setSlice(Slice slice) {
- SliceItem color = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- mSlice = slice;
- List<SliceItem> items = new ArrayList<>();
- boolean[] hasHeader = new boolean[1];
- if (slice.hasHint(Slice.HINT_LIST)) {
- addList(slice, items);
- } else {
- slice.getItems().forEach(item -> {
- if (item.hasHint(Slice.HINT_HIDDEN)) {
- // If it's hidden we don't show it
- return;
- } else if (item.hasHint(Slice.HINT_ACTIONS)) {
- // Action groups don't show in lists
- return;
- } else if (item.getType() == SliceItem.TYPE_COLOR) {
- // A color is not a list item
- return;
- } else if (item.getType() == SliceItem.TYPE_SLICE
- && item.hasHint(Slice.HINT_LIST)) {
- addList(item.getSlice(), items);
- } else if (item.hasHint(Slice.HINT_LIST_ITEM)) {
- items.add(item);
- } else if (!hasHeader[0]) {
- hasHeader[0] = true;
- items.add(0, item);
- } else {
- item.addHint(Slice.HINT_LIST_ITEM);
- items.add(item);
- }
- });
- }
- mAdapter.setSliceItems(items, color);
- }
-
- private void addList(Slice slice, List<SliceItem> items) {
- List<SliceItem> sliceItems = slice.getItems();
- sliceItems.forEach(i -> {
- if (!i.hasHint(Slice.HINT_HIDDEN) && i.getType() != SliceItem.TYPE_COLOR) {
- i.addHint(Slice.HINT_LIST_ITEM);
- items.add(i);
- }
- });
- }
-
- /**
- * Whether or not the content in this template should be scrollable.
- */
- public void setScrollable(boolean isScrollable) {
- // TODO -- restrict / enable how much this view can show
- mIsScrollable = isScrollable;
- }
-}
diff --git a/core/java/android/app/slice/widget/MessageView.java b/core/java/android/app/slice/widget/MessageView.java
deleted file mode 100644
index 3124398e174e..000000000000
--- a/core/java/android/app/slice/widget/MessageView.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.text.SpannableStringBuilder;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class MessageView extends LinearLayout implements SliceListView {
-
- private TextView mDetails;
- private ImageView mIcon;
-
- public MessageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mDetails = findViewById(android.R.id.summary);
- mIcon = findViewById(android.R.id.icon);
- }
-
- @Override
- public void setSliceItem(SliceItem slice) {
- SliceItem source = SliceQuery.find(slice, SliceItem.TYPE_IMAGE, Slice.HINT_SOURCE, null);
- if (source != null) {
- final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 24, getContext().getResources().getDisplayMetrics());
- // TODO try and turn this into a drawable
- Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
- Canvas iconCanvas = new Canvas(iconBm);
- Drawable d = source.getIcon().loadDrawable(getContext());
- d.setBounds(0, 0, iconSize, iconSize);
- d.draw(iconCanvas);
- mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
- }
- SpannableStringBuilder builder = new SpannableStringBuilder();
- SliceQuery.findAll(slice, SliceItem.TYPE_TEXT).forEach(text -> {
- if (builder.length() != 0) {
- builder.append('\n');
- }
- builder.append(text.getText());
- });
- mDetails.setText(builder.toString());
- }
-
-}
diff --git a/core/java/android/app/slice/widget/RemoteInputView.java b/core/java/android/app/slice/widget/RemoteInputView.java
deleted file mode 100644
index 6eff5afb13dc..000000000000
--- a/core/java/android/app/slice/widget/RemoteInputView.java
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.animation.Animator;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutManager;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.R;
-
-/**
- * Host for the remote input.
- *
- * @hide
- */
-// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
-public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
-
- private static final String TAG = "RemoteInput";
-
- /**
- * A marker object that let's us easily find views of this class.
- */
- public static final Object VIEW_TAG = new Object();
-
- private RemoteEditText mEditText;
- private ImageButton mSendButton;
- private ProgressBar mProgressBar;
- private PendingIntent mPendingIntent;
- private RemoteInput[] mRemoteInputs;
- private RemoteInput mRemoteInput;
-
- private int mRevealCx;
- private int mRevealCy;
- private int mRevealR;
- private boolean mResetting;
-
- public RemoteInputView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mProgressBar = findViewById(R.id.remote_input_progress);
- mSendButton = findViewById(R.id.remote_input_send);
- mSendButton.setOnClickListener(this);
-
- mEditText = (RemoteEditText) getChildAt(0);
- mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- final boolean isSoftImeEvent = event == null
- && (actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT
- || actionId == EditorInfo.IME_ACTION_SEND);
- final boolean isKeyboardEnterKey = event != null
- && KeyEvent.isConfirmKey(event.getKeyCode())
- && event.getAction() == KeyEvent.ACTION_DOWN;
-
- if (isSoftImeEvent || isKeyboardEnterKey) {
- if (mEditText.length() > 0) {
- sendRemoteInput();
- }
- // Consume action to prevent IME from closing.
- return true;
- }
- return false;
- }
- });
- mEditText.addTextChangedListener(this);
- mEditText.setInnerFocusable(false);
- mEditText.mRemoteInputView = this;
- }
-
- private void sendRemoteInput() {
- Bundle results = new Bundle();
- results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
- Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
- results);
-
- mEditText.setEnabled(false);
- mSendButton.setVisibility(INVISIBLE);
- mProgressBar.setVisibility(VISIBLE);
- mEditText.mShowImeOnInputConnection = false;
-
- // Tell ShortcutManager that this package has been "activated". ShortcutManager
- // will reset the throttling for this package.
- // Strictly speaking, the intent receiver may be different from the intent creator,
- // but that's an edge case, and also because we can't always know which package will receive
- // an intent, so we just reset for the creator.
- getContext().getSystemService(ShortcutManager.class).onApplicationActive(
- mPendingIntent.getCreatorPackage(),
- getContext().getUserId());
-
- try {
- mPendingIntent.send(mContext, 0, fillInIntent);
- reset();
- } catch (PendingIntent.CanceledException e) {
- Log.i(TAG, "Unable to send remote input result", e);
- Toast.makeText(mContext, "Failure sending pending intent for inline reply :(",
- Toast.LENGTH_SHORT).show();
- reset();
- }
- }
-
- /**
- * Creates a remote input view.
- */
- public static RemoteInputView inflate(Context context, ViewGroup root) {
- RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(
- R.layout.slice_remote_input, root, false);
- v.setTag(VIEW_TAG);
- return v;
- }
-
- @Override
- public void onClick(View v) {
- if (v == mSendButton) {
- sendRemoteInput();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- super.onTouchEvent(event);
-
- // We never want for a touch to escape to an outer view or one we covered.
- return true;
- }
-
- private void onDefocus() {
- setVisibility(INVISIBLE);
- }
-
- /**
- * Set the pending intent for remote input.
- */
- public void setPendingIntent(PendingIntent pendingIntent) {
- mPendingIntent = pendingIntent;
- }
-
- /**
- * Set the remote inputs for this view.
- */
- public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
- mRemoteInputs = remoteInputs;
- mRemoteInput = remoteInput;
- mEditText.setHint(mRemoteInput.getLabel());
- }
-
- /**
- * Focuses the remote input view.
- */
- public void focusAnimated() {
- if (getVisibility() != VISIBLE) {
- Animator animator = ViewAnimationUtils.createCircularReveal(
- this, mRevealCx, mRevealCy, 0, mRevealR);
- animator.setDuration(200);
- animator.start();
- }
- focus();
- }
-
- private void focus() {
- setVisibility(VISIBLE);
- mEditText.setInnerFocusable(true);
- mEditText.mShowImeOnInputConnection = true;
- mEditText.setSelection(mEditText.getText().length());
- mEditText.requestFocus();
- updateSendButton();
- }
-
- private void reset() {
- mResetting = true;
-
- mEditText.getText().clear();
- mEditText.setEnabled(true);
- mSendButton.setVisibility(VISIBLE);
- mProgressBar.setVisibility(INVISIBLE);
- updateSendButton();
- onDefocus();
-
- mResetting = false;
- }
-
- @Override
- public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
- if (mResetting && child == mEditText) {
- // Suppress text events if it happens during resetting. Ideally this would be
- // suppressed by the text view not being shown, but that doesn't work here because it
- // needs to stay visible for the animation.
- return false;
- }
- return super.onRequestSendAccessibilityEvent(child, event);
- }
-
- private void updateSendButton() {
- mSendButton.setEnabled(mEditText.getText().length() != 0);
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- updateSendButton();
- }
-
- /**
- * Tries to find an action that matches the current pending intent of this view and updates its
- * state to that of the found action
- *
- * @return true if a matching action was found, false otherwise
- */
- public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
- if (mPendingIntent == null || actions == null) {
- return false;
- }
- Intent current = mPendingIntent.getIntent();
- if (current == null) {
- return false;
- }
-
- for (Notification.Action a : actions) {
- RemoteInput[] inputs = a.getRemoteInputs();
- if (a.actionIntent == null || inputs == null) {
- continue;
- }
- Intent candidate = a.actionIntent.getIntent();
- if (!current.filterEquals(candidate)) {
- continue;
- }
-
- RemoteInput input = null;
- for (RemoteInput i : inputs) {
- if (i.getAllowFreeFormInput()) {
- input = i;
- }
- }
- if (input == null) {
- continue;
- }
- setPendingIntent(a.actionIntent);
- setRemoteInput(inputs, input);
- return true;
- }
- return false;
- }
-
- /**
- * @hide
- */
- public void setRevealParameters(int cx, int cy, int r) {
- mRevealCx = cx;
- mRevealCy = cy;
- mRevealR = r;
- }
-
- @Override
- public void dispatchStartTemporaryDetach() {
- super.dispatchStartTemporaryDetach();
- // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
- // won't lose IME focus.
- detachViewFromParent(mEditText);
- }
-
- @Override
- public void dispatchFinishTemporaryDetach() {
- if (isAttachedToWindow()) {
- attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
- } else {
- removeDetachedView(mEditText, false /* animate */);
- }
- super.dispatchFinishTemporaryDetach();
- }
-
- /**
- * An EditText that changes appearance based on whether it's focusable and becomes un-focusable
- * whenever the user navigates away from it or it becomes invisible.
- */
- public static class RemoteEditText extends EditText {
-
- private final Drawable mBackground;
- private RemoteInputView mRemoteInputView;
- boolean mShowImeOnInputConnection;
-
- public RemoteEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- mBackground = getBackground();
- }
-
- private void defocusIfNeeded(boolean animate) {
- if (mRemoteInputView != null || isTemporarilyDetached()) {
- if (isTemporarilyDetached()) {
- // We might get reattached but then the other one of HUN / expanded might steal
- // our focus, so we'll need to save our text here.
- }
- return;
- }
- if (isFocusable() && isEnabled()) {
- setInnerFocusable(false);
- if (mRemoteInputView != null) {
- mRemoteInputView.onDefocus();
- }
- mShowImeOnInputConnection = false;
- }
- }
-
- @Override
- protected void onVisibilityChanged(View changedView, int visibility) {
- super.onVisibilityChanged(changedView, visibility);
-
- if (!isShown()) {
- defocusIfNeeded(false /* animate */);
- }
- }
-
- @Override
- protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(focused, direction, previouslyFocusedRect);
- if (!focused) {
- defocusIfNeeded(true /* animate */);
- }
- }
-
- @Override
- public void getFocusedRect(Rect r) {
- super.getFocusedRect(r);
- r.top = mScrollY;
- r.bottom = mScrollY + (mBottom - mTop);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- // Eat the DOWN event here to prevent any default behavior.
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- defocusIfNeeded(true /* animate */);
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
-
- if (mShowImeOnInputConnection && inputConnection != null) {
- final InputMethodManager imm = InputMethodManager.getInstance();
- if (imm != null) {
- // onCreateInputConnection is called by InputMethodManager in the middle of
- // setting up the connection to the IME; wait with requesting the IME until that
- // work has completed.
- post(new Runnable() {
- @Override
- public void run() {
- imm.viewClicked(RemoteEditText.this);
- imm.showSoftInput(RemoteEditText.this, 0);
- }
- });
- }
- }
-
- return inputConnection;
- }
-
- @Override
- public void onCommitCompletion(CompletionInfo text) {
- clearComposingText();
- setText(text.getText());
- setSelection(getText().length());
- }
-
- void setInnerFocusable(boolean focusable) {
- setFocusableInTouchMode(focusable);
- setFocusable(focusable);
- setCursorVisible(focusable);
-
- if (focusable) {
- requestFocus();
- setBackground(mBackground);
- } else {
- setBackground(null);
- }
-
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/ShortcutView.java b/core/java/android/app/slice/widget/ShortcutView.java
deleted file mode 100644
index 0b7ad0d667dd..000000000000
--- a/core/java/android/app/slice/widget/ShortcutView.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
-import android.net.Uri;
-
-import com.android.internal.R;
-
-import java.util.List;
-
-/**
- * @hide
- */
-public class ShortcutView extends SliceModeView {
-
- private static final String TAG = "ShortcutView";
-
- private Uri mUri;
- private PendingIntent mAction;
- private SliceItem mLabel;
- private SliceItem mIcon;
-
- private int mLargeIconSize;
- private int mSmallIconSize;
-
- public ShortcutView(Context context) {
- super(context);
- final Resources res = getResources();
- mSmallIconSize = res.getDimensionPixelSize(R.dimen.slice_icon_size);
- mLargeIconSize = res.getDimensionPixelSize(R.dimen.slice_shortcut_size);
- }
-
- @Override
- public void setSlice(Slice slice) {
- removeAllViews();
- determineShortcutItems(getContext(), slice);
- SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- if (colorItem == null) {
- colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- }
- // TODO: pick better default colour
- final int color = colorItem != null ? colorItem.getColor() : Color.GRAY;
- ShapeDrawable circle = new ShapeDrawable(new OvalShape());
- circle.setTint(color);
- setBackground(circle);
- if (mIcon != null) {
- final boolean isLarge = mIcon.hasHint(Slice.HINT_LARGE);
- final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
- SliceViewUtil.createCircledIcon(getContext(), color, iconSize, mIcon.getIcon(),
- isLarge, this /* parent */);
- mUri = slice.getUri();
- setClickable(true);
- } else {
- setClickable(false);
- }
- }
-
- @Override
- public String getMode() {
- return SliceView.MODE_SHORTCUT;
- }
-
- @Override
- public boolean performClick() {
- if (!callOnClick()) {
- try {
- if (mAction != null) {
- mAction.send();
- } else {
- Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
- }
- } catch (CanceledException e) {
- e.printStackTrace();
- }
- }
- return true;
- }
-
- /**
- * Looks at the slice and determines which items are best to use to compose the shortcut.
- */
- private void determineShortcutItems(Context context, Slice slice) {
- List<String> h = slice.getHints();
- SliceItem sliceItem = new SliceItem(slice, SliceItem.TYPE_SLICE,
- h.toArray(new String[h.size()]));
- SliceItem titleItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION,
- Slice.HINT_TITLE, null);
-
- if (titleItem != null) {
- // Preferred case: hinted action containing hinted image and text
- mAction = titleItem.getAction();
- mIcon = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
- null);
- mLabel = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
- null);
- } else {
- // No hinted action; just use the first one
- SliceItem actionItem = SliceQuery.find(sliceItem, SliceItem.TYPE_ACTION, (String) null,
- null);
- mAction = (actionItem != null) ? actionItem.getAction() : null;
- }
- // First fallback: any hinted image and text
- if (mIcon == null) {
- mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
- null);
- }
- if (mLabel == null) {
- mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
- null);
- }
- // Second fallback: first image and text
- if (mIcon == null) {
- mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, (String) null,
- null);
- }
- if (mLabel == null) {
- mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, (String) null,
- null);
- }
- // Final fallback: use app info
- if (mIcon == null || mLabel == null || mAction == null) {
- PackageManager pm = context.getPackageManager();
- ProviderInfo providerInfo = pm.resolveContentProvider(
- slice.getUri().getAuthority(), 0);
- ApplicationInfo appInfo = providerInfo.applicationInfo;
- if (appInfo != null) {
- if (mIcon == null) {
- Drawable icon = appInfo.loadDefaultIcon(pm);
- mIcon = new SliceItem(SliceViewUtil.createIconFromDrawable(icon),
- SliceItem.TYPE_IMAGE, new String[] {Slice.HINT_LARGE});
- }
- if (mLabel == null) {
- mLabel = new SliceItem(pm.getApplicationLabel(appInfo),
- SliceItem.TYPE_TEXT, null);
- }
- if (mAction == null) {
- mAction = PendingIntent.getActivity(context, 0,
- pm.getLaunchIntentForPackage(appInfo.packageName), 0);
- }
- }
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/SliceView.java b/core/java/android/app/slice/widget/SliceView.java
deleted file mode 100644
index fa1b64ceed97..000000000000
--- a/core/java/android/app/slice/widget/SliceView.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.graphics.drawable.ColorDrawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-
-/**
- * A view for displaying a {@link Slice} which is a piece of app content and actions. SliceView is
- * able to present slice content in a templated format outside of the associated app. The way this
- * content is displayed depends on the structure of the slice, the hints associated with the
- * content, and the mode that SliceView is configured for. The modes that SliceView supports are:
- * <ul>
- * <li><b>Shortcut</b>: A shortcut is presented as an icon and a text label representing the main
- * content or action associated with the slice.</li>
- * <li><b>Small</b>: The small format has a restricted height and can present a single
- * {@link SliceItem} or a limited collection of items.</li>
- * <li><b>Large</b>: The large format displays multiple small templates in a list, if scrolling is
- * not enabled (see {@link #setScrollable(boolean)}) the view will show as many items as it can
- * comfortably fit.</li>
- * </ul>
- * <p>
- * When constructing a slice, the contents of it can be annotated with hints, these provide the OS
- * with some information on how the content should be displayed. For example, text annotated with
- * {@link Slice#HINT_TITLE} would be placed in the title position of a template. A slice annotated
- * with {@link Slice#HINT_LIST} would present the child items of that slice in a list.
- * <p>
- * SliceView can be provided a slice via a uri {@link #setSlice(Uri)} in which case a content
- * observer will be set for that uri and the view will update if there are any changes to the slice.
- * To use this the app must have a special permission to bind to the slice (see
- * {@link android.Manifest.permission#BIND_SLICE}).
- * <p>
- * Example usage:
- *
- * <pre class="prettyprint">
- * SliceView v = new SliceView(getContext());
- * v.setMode(desiredMode);
- * v.setSlice(sliceUri);
- * </pre>
- */
-public class SliceView extends ViewGroup {
-
- private static final String TAG = "SliceView";
-
- /**
- * @hide
- */
- public abstract static class SliceModeView extends FrameLayout {
-
- public SliceModeView(Context context) {
- super(context);
- }
-
- /**
- * @return the mode of the slice being presented.
- */
- public abstract String getMode();
-
- /**
- * @param slice the slice to show in this view.
- */
- public abstract void setSlice(Slice slice);
- }
-
- /**
- * @hide
- */
- @StringDef({
- MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
- })
- public @interface SliceMode {}
-
- /**
- * Mode indicating this slice should be presented in small template format.
- */
- public static final String MODE_SMALL = "SLICE_SMALL";
- /**
- * Mode indicating this slice should be presented in large template format.
- */
- public static final String MODE_LARGE = "SLICE_LARGE";
- /**
- * Mode indicating this slice should be presented as an icon. A shortcut requires an intent,
- * icon, and label. This can be indicated by using {@link Slice#HINT_TITLE} on an action in a
- * slice.
- */
- public static final String MODE_SHORTCUT = "SLICE_ICON";
-
- /**
- * Will select the type of slice binding based on size of the View. TODO: Put in some info about
- * that selection.
- */
- private static final String MODE_AUTO = "auto";
-
- private String mMode = MODE_AUTO;
- private SliceModeView mCurrentView;
- private final ActionRow mActions;
- private Slice mCurrentSlice;
- private boolean mShowActions = true;
- private boolean mIsScrollable;
- private SliceObserver mObserver;
- private final int mShortcutSize;
-
- public SliceView(Context context) {
- this(context, null);
- }
-
- public SliceView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
- mActions = new ActionRow(mContext, true);
- mActions.setBackground(new ColorDrawable(0xffeeeeee));
- mCurrentView = new LargeTemplateView(mContext);
- addView(mCurrentView, getChildLp(mCurrentView));
- addView(mActions, getChildLp(mActions));
- mShortcutSize = getContext().getResources()
- .getDimensionPixelSize(R.dimen.slice_shortcut_size);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- measureChildren(widthMeasureSpec, heightMeasureSpec);
- int actionHeight = mActions.getVisibility() != View.GONE
- ? mActions.getMeasuredHeight()
- : 0;
- int newHeightSpec = MeasureSpec.makeMeasureSpec(
- mCurrentView.getMeasuredHeight() + actionHeight, MeasureSpec.EXACTLY);
- int width = MeasureSpec.getSize(widthMeasureSpec);
- setMeasuredDimension(width, newHeightSpec);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- mCurrentView.layout(l, t, l + mCurrentView.getMeasuredWidth(),
- t + mCurrentView.getMeasuredHeight());
- if (mActions.getVisibility() != View.GONE) {
- mActions.layout(l, mCurrentView.getMeasuredHeight(), l + mActions.getMeasuredWidth(),
- mCurrentView.getMeasuredHeight() + mActions.getMeasuredHeight());
- }
- }
-
- /**
- * Populates this view with the {@link Slice} associated with the provided {@link Intent}. To
- * use this method your app must have the permission
- * {@link android.Manifest.permission#BIND_SLICE}).
- * <p>
- * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is
- * updated with the slice identified by the provided intent changes. The lifecycle of this
- * observer is handled by SliceView in {@link #onAttachedToWindow()} and
- * {@link #onDetachedFromWindow()}. To unregister this observer outside of that you can call
- * {@link #clearSlice}.
- *
- * @return true if a slice was found for the provided intent.
- * @hide
- */
- public boolean setSlice(@Nullable Intent intent) {
- Slice s = Slice.bindSlice(mContext, intent);
- if (s != null) {
- return setSlice(s.getUri());
- }
- return s != null;
- }
-
- /**
- * Populates this view with the {@link Slice} associated with the provided {@link Uri}. To use
- * this method your app must have the permission
- * {@link android.Manifest.permission#BIND_SLICE}).
- * <p>
- * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is
- * updated when the slice identified by the provided URI changes. The lifecycle of this observer
- * is handled by SliceView in {@link #onAttachedToWindow()} and {@link #onDetachedFromWindow()}.
- * To unregister this observer outside of that you can call {@link #clearSlice}.
- *
- * @return true if a slice was found for the provided uri.
- */
- public boolean setSlice(@NonNull Uri sliceUri) {
- Preconditions.checkNotNull(sliceUri,
- "Uri cannot be null, to remove the slice use clearSlice()");
- if (sliceUri == null) {
- clearSlice();
- return false;
- }
- validate(sliceUri);
- Slice s = Slice.bindSlice(mContext.getContentResolver(), sliceUri);
- if (s != null) {
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- }
- mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
- if (isAttachedToWindow()) {
- registerSlice(sliceUri);
- }
- mCurrentSlice = s;
- reinflate();
- }
- return s != null;
- }
-
- /**
- * Populates this view to the provided {@link Slice}.
- * <p>
- * This does not register a content observer on the URI that the slice is backed by so it will
- * not update if the content changes. To have the view update when the content changes use
- * {@link #setSlice(Uri)} instead. Unlike {@link #setSlice(Uri)}, this method does not require
- * any special permissions.
- */
- public void showSlice(@NonNull Slice slice) {
- Preconditions.checkNotNull(slice,
- "Slice cannot be null, to remove the slice use clearSlice()");
- clearSlice();
- mCurrentSlice = slice;
- reinflate();
- }
-
- /**
- * Unregisters the change observer that is set when using {@link #setSlice}. Normally this is
- * done automatically during {@link #onDetachedFromWindow()}.
- * <p>
- * It is safe to call this method multiple times.
- */
- public void clearSlice() {
- mCurrentSlice = null;
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- }
-
- /**
- * Set the mode this view should present in.
- */
- public void setMode(@SliceMode String mode) {
- setMode(mode, false /* animate */);
- }
-
- /**
- * Set whether this view should allow scrollable content when presenting in {@link #MODE_LARGE}.
- */
- public void setScrollable(boolean isScrollable) {
- mIsScrollable = isScrollable;
- reinflate();
- }
-
- /**
- * @hide
- */
- public void setMode(@SliceMode String mode, boolean animate) {
- if (animate) {
- Log.e(TAG, "Animation not supported yet");
- }
- mMode = mode;
- reinflate();
- }
-
- /**
- * @return the mode this view is presenting in.
- */
- public @SliceMode String getMode() {
- if (mMode.equals(MODE_AUTO)) {
- return MODE_LARGE;
- }
- return mMode;
- }
-
- /**
- * @hide
- *
- * Whether this view should show a row of actions with it.
- */
- public void setShowActionRow(boolean show) {
- mShowActions = show;
- reinflate();
- }
-
- private SliceModeView createView(String mode) {
- switch (mode) {
- case MODE_SHORTCUT:
- return new ShortcutView(getContext());
- case MODE_SMALL:
- return new SmallTemplateView(getContext());
- }
- return new LargeTemplateView(getContext());
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- registerSlice(mCurrentSlice != null ? mCurrentSlice.getUri() : null);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- }
-
- private void registerSlice(Uri sliceUri) {
- if (sliceUri == null || mObserver == null) {
- return;
- }
- mContext.getContentResolver().registerContentObserver(sliceUri,
- false /* notifyForDescendants */, mObserver);
- }
-
- private void reinflate() {
- if (mCurrentSlice == null) {
- return;
- }
- // TODO: Smarter mapping here from one state to the next.
- SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR);
- List<SliceItem> items = mCurrentSlice.getItems();
- SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE,
- Slice.HINT_ACTIONS,
- Slice.HINT_ALT);
- String mode = getMode();
- if (!mode.equals(mCurrentView.getMode())) {
- removeAllViews();
- mCurrentView = createView(mode);
- addView(mCurrentView, getChildLp(mCurrentView));
- addView(mActions, getChildLp(mActions));
- }
- if (mode.equals(MODE_LARGE)) {
- ((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
- }
- if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) {
- mCurrentView.setVisibility(View.VISIBLE);
- mCurrentView.setSlice(mCurrentSlice);
- } else {
- mCurrentView.setVisibility(View.GONE);
- }
-
- boolean showActions = mShowActions && actionRow != null
- && !mode.equals(MODE_SHORTCUT);
- if (showActions) {
- mActions.setActions(actionRow, color);
- mActions.setVisibility(View.VISIBLE);
- } else {
- mActions.setVisibility(View.GONE);
- }
- }
-
- private LayoutParams getChildLp(View child) {
- if (child instanceof ShortcutView) {
- return new LayoutParams(mShortcutSize, mShortcutSize);
- } else {
- return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
- }
-
- private static void validate(Uri sliceUri) {
- if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) {
- throw new RuntimeException("Invalid uri " + sliceUri);
- }
- if (sliceUri.getPathSegments().size() == 0) {
- throw new RuntimeException("Invalid uri " + sliceUri);
- }
- }
-
- private class SliceObserver extends ContentObserver {
- SliceObserver(Handler handler) {
- super(handler);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- this.onChange(selfChange, null);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- Slice s = Slice.bindSlice(mContext.getContentResolver(), uri);
- mCurrentSlice = s;
- reinflate();
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/SliceViewUtil.java b/core/java/android/app/slice/widget/SliceViewUtil.java
deleted file mode 100644
index 1cf0055bcfe8..000000000000
--- a/core/java/android/app/slice/widget/SliceViewUtil.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.annotation.ColorInt;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-/**
- * A bunch of utilities for slice UI.
- *
- * @hide
- */
-public class SliceViewUtil {
-
- /**
- * @hide
- */
- @ColorInt
- public static int getColorAccent(Context context) {
- return getColorAttr(context, android.R.attr.colorAccent);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getColorError(Context context) {
- return getColorAttr(context, android.R.attr.colorError);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getDefaultColor(Context context, int resId) {
- final ColorStateList list = context.getResources().getColorStateList(resId,
- context.getTheme());
-
- return list.getDefaultColor();
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getDisabled(Context context, int inputColor) {
- return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int applyAlphaAttr(Context context, int attr, int inputColor) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- float alpha = ta.getFloat(0, 0);
- ta.recycle();
- return applyAlpha(alpha, inputColor);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int applyAlpha(float alpha, int inputColor) {
- alpha *= Color.alpha(inputColor);
- return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
- Color.blue(inputColor));
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getColorAttr(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- @ColorInt
- int colorAccent = ta.getColor(0, 0);
- ta.recycle();
- return colorAccent;
- }
-
- /**
- * @hide
- */
- public static int getThemeAttr(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- int theme = ta.getResourceId(0, 0);
- ta.recycle();
- return theme;
- }
-
- /**
- * @hide
- */
- public static Drawable getDrawable(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- Drawable drawable = ta.getDrawable(0);
- ta.recycle();
- return drawable;
- }
-
- /**
- * @hide
- */
- public static Icon createIconFromDrawable(Drawable d) {
- if (d instanceof BitmapDrawable) {
- return Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
- }
- Bitmap b = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(b);
- d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- d.draw(canvas);
- return Icon.createWithBitmap(b);
- }
-
- /**
- * @hide
- */
- public static void createCircledIcon(Context context, int color, int iconSize, Icon icon,
- boolean isLarge, ViewGroup parent) {
- ImageView v = new ImageView(context);
- v.setImageIcon(icon);
- parent.addView(v);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
- if (isLarge) {
- // XXX better way to convert from icon -> bitmap or crop an icon (?)
- Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
- Canvas iconCanvas = new Canvas(iconBm);
- v.layout(0, 0, iconSize, iconSize);
- v.draw(iconCanvas);
- v.setImageBitmap(getCircularBitmap(iconBm));
- } else {
- v.setColorFilter(Color.WHITE);
- }
- lp.width = iconSize;
- lp.height = iconSize;
- lp.gravity = Gravity.CENTER;
- }
-
- /**
- * @hide
- */
- public static Bitmap getCircularBitmap(Bitmap bitmap) {
- Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
- bitmap.getHeight(), Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
- final Paint paint = new Paint();
- final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
- paint.setAntiAlias(true);
- canvas.drawARGB(0, 0, 0, 0);
- canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
- bitmap.getWidth() / 2, paint);
- paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
- canvas.drawBitmap(bitmap, rect, rect, paint);
- return output;
- }
-}
diff --git a/core/java/android/app/slice/widget/SmallTemplateView.java b/core/java/android/app/slice/widget/SmallTemplateView.java
deleted file mode 100644
index 1c4c5df2d426..000000000000
--- a/core/java/android/app/slice/widget/SmallTemplateView.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.text.Format;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Small template is also used to construct list items for use with {@link LargeTemplateView}.
- *
- * @hide
- */
-public class SmallTemplateView extends SliceModeView implements SliceListView {
-
- private static final String TAG = "SmallTemplateView";
-
- private int mIconSize;
- private int mPadding;
-
- private LinearLayout mStartContainer;
- private TextView mTitleText;
- private TextView mSecondaryText;
- private LinearLayout mEndContainer;
-
- public SmallTemplateView(Context context) {
- super(context);
- inflate(context, R.layout.slice_small_template, this);
- mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
- mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.slice_padding);
-
- mStartContainer = (LinearLayout) findViewById(android.R.id.icon_frame);
- mTitleText = (TextView) findViewById(android.R.id.title);
- mSecondaryText = (TextView) findViewById(android.R.id.summary);
- mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
- }
-
- @Override
- public String getMode() {
- return SliceView.MODE_SMALL;
- }
-
- @Override
- public void setSliceItem(SliceItem slice) {
- resetViews();
- SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- int color = colorItem != null ? colorItem.getColor() : -1;
-
- // Look for any title elements
- List<SliceItem> titleItems = SliceQuery.findAll(slice, -1, Slice.HINT_TITLE,
- null);
- boolean hasTitleText = false;
- boolean hasTitleItem = false;
- for (int i = 0; i < titleItems.size(); i++) {
- SliceItem item = titleItems.get(i);
- if (!hasTitleItem) {
- // icon, action icon, or timestamp
- if (item.getType() == SliceItem.TYPE_ACTION) {
- hasTitleItem = addIcon(item, color, mStartContainer);
- } else if (item.getType() == SliceItem.TYPE_IMAGE) {
- addIcon(item, color, mStartContainer);
- hasTitleItem = true;
- } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
- TextView tv = new TextView(getContext());
- tv.setText(convertTimeToString(item.getTimestamp()));
- hasTitleItem = true;
- }
- }
- if (!hasTitleText && item.getType() == SliceItem.TYPE_TEXT) {
- mTitleText.setText(item.getText());
- hasTitleText = true;
- }
- if (hasTitleText && hasTitleItem) {
- break;
- }
- }
- mTitleText.setVisibility(hasTitleText ? View.VISIBLE : View.GONE);
- mStartContainer.setVisibility(hasTitleItem ? View.VISIBLE : View.GONE);
-
- if (slice.getType() != SliceItem.TYPE_SLICE) {
- return;
- }
-
- // Deal with remaining items
- int itemCount = 0;
- boolean hasSummary = false;
- ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>(
- slice.getSlice().getItems());
- for (int i = 0; i < sliceItems.size(); i++) {
- SliceItem item = sliceItems.get(i);
- if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT
- && !item.hasHint(Slice.HINT_TITLE)) {
- // TODO -- Should combine all text items?
- mSecondaryText.setText(item.getText());
- hasSummary = true;
- }
- if (itemCount <= 3) {
- if (item.getType() == SliceItem.TYPE_ACTION) {
- if (addIcon(item, color, mEndContainer)) {
- itemCount++;
- }
- } else if (item.getType() == SliceItem.TYPE_IMAGE) {
- addIcon(item, color, mEndContainer);
- itemCount++;
- } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
- TextView tv = new TextView(getContext());
- tv.setText(convertTimeToString(item.getTimestamp()));
- mEndContainer.addView(tv);
- itemCount++;
- } else if (item.getType() == SliceItem.TYPE_SLICE) {
- List<SliceItem> subItems = item.getSlice().getItems();
- for (int j = 0; j < subItems.size(); j++) {
- sliceItems.add(subItems.get(j));
- }
- }
- }
- }
- }
-
- @Override
- public void setSlice(Slice slice) {
- setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE,
- slice.getHints().toArray(new String[slice.getHints().size()])));
- }
-
- /**
- * @return Whether an icon was added.
- */
- private boolean addIcon(SliceItem sliceItem, int color, LinearLayout container) {
- SliceItem image = null;
- SliceItem action = null;
- if (sliceItem.getType() == SliceItem.TYPE_ACTION) {
- image = SliceQuery.find(sliceItem.getSlice(), SliceItem.TYPE_IMAGE);
- action = sliceItem;
- } else if (sliceItem.getType() == SliceItem.TYPE_IMAGE) {
- image = sliceItem;
- }
- if (image != null) {
- ImageView iv = new ImageView(getContext());
- iv.setImageIcon(image.getIcon());
- if (action != null) {
- final SliceItem sliceAction = action;
- iv.setOnClickListener(v -> AsyncTask.execute(
- () -> {
- try {
- sliceAction.getAction().send();
- } catch (CanceledException e) {
- e.printStackTrace();
- }
- }));
- iv.setBackground(SliceViewUtil.getDrawable(getContext(),
- android.R.attr.selectableItemBackground));
- }
- if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) {
- iv.setColorFilter(color);
- }
- container.addView(iv);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
- lp.width = mIconSize;
- lp.height = mIconSize;
- lp.setMarginStart(mPadding);
- return true;
- }
- return false;
- }
-
- private String convertTimeToString(long time) {
- // TODO -- figure out what format(s) we support
- Date date = new Date(time);
- Format format = new SimpleDateFormat("MM dd yyyy HH:mm:ss");
- return format.format(date);
- }
-
- private void resetViews() {
- mStartContainer.removeAllViews();
- mEndContainer.removeAllViews();
- mTitleText.setText(null);
- mSecondaryText.setText(null);
- }
-}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0d7a94138411..8200414fa6bd 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -100,6 +100,12 @@ public final class UsageEvents implements Parcelable {
*/
public static final int CHOOSER_ACTION = 9;
+ /**
+ * An event type denoting that a notification was viewed by the user.
+ * @hide
+ */
+ public static final int NOTIFICATION_SEEN = 10;
+
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index e3d763ab9b1e..6692e137fa0a 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -350,13 +350,22 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* application can be registered at time. When no longer used, application
* should be unregistered using
* {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}.
+ * The registration status should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related
+ * to the return value of this method.
*
* @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record.
+ * The HID Device SDP record is required.
* @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings.
+ * The Incoming QoS Settings is not required. Use null or default
+ * BluetoothHidDeviceAppQosSettings.Builder for default values.
* @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings.
+ * The Outgoing QoS Settings is not required. Use null or default
+ * BluetoothHidDeviceAppQosSettings.Builder for default values.
* @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be
* sent.
- * @return
+ * The BluetoothHidDeviceCallback object is required.
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
@@ -394,12 +403,15 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* {@link #registerApp
* (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
* BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)}
+ * The registration status should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related
+ * to the return value of this method.
*
* @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link
* BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice,
* BluetoothHidDeviceAppConfiguration,
* boolean)}
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) {
Log.v(TAG, "unregisterApp()");
@@ -426,7 +438,7 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in
* descriptor.
* @param data Report data, not including Report Id.
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
@@ -452,7 +464,7 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* @param type Report Type, as in request.
* @param id Report Id, as in request.
* @param data Report data, not including Report Id.
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
@@ -478,7 +490,7 @@ public final class BluetoothHidDevice implements BluetoothProfile {
* from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
*
* @param error Error to be sent for SET_REPORT via HANDSHAKE.
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean reportError(BluetoothDevice device, byte error) {
Log.v(TAG, "reportError(): device=" + device + " error=" + error);
@@ -524,10 +536,13 @@ public final class BluetoothHidDevice implements BluetoothProfile {
}
/**
- * Initiates connection to host which currently has Virtual Cable
- * established with device.
+ * Initiates connection to host which is currently paired with this device.
+ * If the application is not registered, #connect(BluetoothDevice) will fail.
+ * The connection state should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related
+ * to the return value of this method.
*
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean connect(BluetoothDevice device) {
Log.v(TAG, "connect(): device=" + device);
@@ -550,8 +565,11 @@ public final class BluetoothHidDevice implements BluetoothProfile {
/**
* Disconnects from currently connected host.
+ * The connection state should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related
+ * to the return value of this method.
*
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean disconnect(BluetoothDevice device) {
Log.v(TAG, "disconnect(): device=" + device);
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
index ccc3ef4008fa..881ae98d9889 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -45,6 +45,21 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
public static final int MAX = (int) 0xffffffff;
+ /**
+ * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel.
+ * The QoS Settings is optional.
+ * Recommended to use BluetoothHidDeviceAppQosSettings.Builder.
+ * {@see <a href="https://www.bluetooth.com/specifications/profiles-overview">
+ * https://www.bluetooth.com/specifications/profiles-overview
+ * </a>
+ * Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D }
+ * @param serviceType L2CAP service type
+ * @param tokenRate L2CAP token rate
+ * @param tokenBucketSize L2CAP token bucket size
+ * @param peakBandwidth L2CAP peak bandwidth
+ * @param latency L2CAP latency
+ * @param delayVariation L2CAP delay variation
+ */
public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize,
int peakBandwidth, int latency, int delayVariation) {
this.serviceType = serviceType;
@@ -59,7 +74,12 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
public boolean equals(Object o) {
if (o instanceof BluetoothHidDeviceAppQosSettings) {
BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o;
- return false;
+ return this.serviceType == qos.serviceType
+ && this.tokenRate == qos.tokenRate
+ && this.tokenBucketSize == qos.tokenBucketSize
+ && this.peakBandwidth == qos.peakBandwidth
+ && this.latency == qos.latency
+ && this.delayVariation == qos.delayVariation;
}
return false;
}
@@ -106,4 +126,85 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
};
}
+
+ /**
+ * A helper to build the BluetoothHidDeviceAppQosSettings object.
+ */
+ public static class Builder {
+ // Optional parameters - initialized to default values
+ private int mServiceType = SERVICE_BEST_EFFORT;
+ private int mTokenRate = 0;
+ private int mTokenBucketSize = 0;
+ private int mPeakBandwidth = 0;
+ private int mLatency = MAX;
+ private int mDelayVariation = MAX;
+
+ /**
+ * Set the service type.
+ * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT,
+ * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one.
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified service type.
+ */
+ public Builder serviceType(int val) {
+ mServiceType = val;
+ return this;
+ }
+ /**
+ * Set the token rate.
+ * @param val token rate
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate.
+ */
+ public Builder tokenRate(int val) {
+ mTokenRate = val;
+ return this;
+ }
+
+ /**
+ * Set the bucket size.
+ * @param val bucket size
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size.
+ */
+ public Builder tokenBucketSize(int val) {
+ mTokenBucketSize = val;
+ return this;
+ }
+
+ /**
+ * Set the peak bandwidth.
+ * @param val peak bandwidth
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth.
+ */
+ public Builder peakBandwidth(int val) {
+ mPeakBandwidth = val;
+ return this;
+ }
+ /**
+ * Set the latency.
+ * @param val latency
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified latency.
+ */
+ public Builder latency(int val) {
+ mLatency = val;
+ return this;
+ }
+
+ /**
+ * Set the delay variation.
+ * @param val delay variation
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation.
+ */
+ public Builder delayVariation(int val) {
+ mDelayVariation = val;
+ return this;
+ }
+
+ /**
+ * Build the BluetoothHidDeviceAppQosSettings object.
+ * @return BluetoothHidDeviceAppQosSettings object with current settings.
+ */
+ public BluetoothHidDeviceAppQosSettings build() {
+ return new BluetoothHidDeviceAppQosSettings(mServiceType, mTokenRate, mTokenBucketSize,
+ mPeakBandwidth, mLatency, mDelayVariation);
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index f01c49328968..4669637043af 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -19,6 +19,8 @@ package android.bluetooth;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+
/**
* Represents the Service Discovery Protocol (SDP) settings for a Bluetooth
* HID Device application.
@@ -39,6 +41,18 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
public final byte subclass;
public final byte[] descriptors;
+ /**
+ * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record.
+ * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes.
+ * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes.
+ * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes.
+ * @param subclass Subclass of this Bluetooth HID device.
+ * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf">
+ * www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2</a>
+ * @param descriptors Descriptors of this Bluetooth HID device.
+ * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf">
+ * www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6</a> Maximum length is 2048 bytes.
+ */
public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider,
byte subclass, byte[] descriptors) {
this.name = name;
@@ -52,7 +66,11 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
public boolean equals(Object o) {
if (o instanceof BluetoothHidDeviceAppSdpSettings) {
BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o;
- return false;
+ return this.name.equals(sdp.name)
+ && this.description.equals(sdp.description)
+ && this.provider.equals(sdp.provider)
+ && this.subclass == sdp.subclass
+ && Arrays.equals(this.descriptors, sdp.descriptors);
}
return false;
}
diff --git a/core/java/android/content/pm/IPackageInstallObserver.aidl b/core/java/android/content/pm/IPackageInstallObserver.aidl
deleted file mode 100644
index 613336537317..000000000000
--- a/core/java/android/content/pm/IPackageInstallObserver.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.content.pm;
-
-/**
- * API for installation callbacks from the Package Manager.
- * @hide
- */
-oneway interface IPackageInstallObserver {
- void packageInstalled(in String packageName, int returnCode);
-}
-
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1352bc2214d4..64d33d543e4a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -543,9 +543,10 @@ interface IPackageManager {
void forceDexOpt(String packageName);
/**
- * Execute the background dexopt job immediately.
+ * Execute the background dexopt job immediately on packages in packageNames.
+ * If null, then execute on all packages.
*/
- boolean runBackgroundDexoptJob();
+ boolean runBackgroundDexoptJob(in List<String> packageNames);
/**
* Reconcile the information we have about the secondary dex files belonging to
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 86288396ad84..77c5743fc882 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -81,6 +81,9 @@ import java.util.List;
* <li>All APKs must have unique split names.
* <li>All installations must contain a single base APK.
* </ul>
+ * <p>
+ * The ApiDemos project contains examples of using this API:
+ * <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
*/
public class PackageInstaller {
private static final String TAG = "PackageInstaller";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 31ca19859292..15ea4b53083d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -871,8 +871,8 @@ public abstract class PackageManager {
public static final int INSTALL_REASON_USER = 4;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} on success.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * on success.
*
* @hide
*/
@@ -880,8 +880,8 @@ public abstract class PackageManager {
public static final int INSTALL_SUCCEEDED = 1;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package is already installed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package is already installed.
*
* @hide
*/
@@ -889,8 +889,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package archive file is invalid.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package archive file is invalid.
*
* @hide
*/
@@ -898,8 +898,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INVALID_APK = -2;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the URI passed in is invalid.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the URI passed in is invalid.
*
* @hide
*/
@@ -907,9 +907,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INVALID_URI = -3;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package manager service found that
- * the device didn't have enough storage space to install the app.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package manager service found that the device didn't have enough storage space to
+ * install the app.
*
* @hide
*/
@@ -917,9 +917,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a package is already installed with
- * the same name.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if a package is already installed with the same name.
*
* @hide
*/
@@ -927,9 +926,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the requested shared user does not
- * exist.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the requested shared user does not exist.
*
* @hide
*/
@@ -937,10 +935,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a previously installed package of the
- * same name has a different signature than the new package (and the old
- * package's data was not removed).
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if a previously installed package of the same name has a different signature than the new
+ * package (and the old package's data was not removed).
*
* @hide
*/
@@ -948,10 +945,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package is requested a shared
- * user which is already installed on the device and does not have matching
- * signature.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package is requested a shared user which is already installed on the device and
+ * does not have matching signature.
*
* @hide
*/
@@ -959,9 +955,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a shared library
- * that is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a shared library that is not available.
*
* @hide
*/
@@ -969,9 +964,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a shared library
- * that is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a shared library that is not available.
*
* @hide
*/
@@ -979,10 +973,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed while
- * optimizing and validating its dex files, either because there was not
- * enough storage or the validation failed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed while optimizing and validating its dex files, either because there
+ * was not enough storage or the validation failed.
*
* @hide
*/
@@ -990,9 +983,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_DEXOPT = -11;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because the
- * current SDK version is older than that required by the package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because the current SDK version is older than that required by the
+ * package.
*
* @hide
*/
@@ -1000,10 +993,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_OLDER_SDK = -12;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because it
- * contains a content provider with the same authority as a provider already
- * installed in the system.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because it contains a content provider with the same authority as a
+ * provider already installed in the system.
*
* @hide
*/
@@ -1011,9 +1003,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because the
- * current SDK version is newer than that required by the package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because the current SDK version is newer than that required by the
+ * package.
*
* @hide
*/
@@ -1021,10 +1013,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_NEWER_SDK = -14;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because it has
- * specified that it is a test-only package and the caller has not supplied
- * the {@link #INSTALL_ALLOW_TEST} flag.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because it has specified that it is a test-only package and the
+ * caller has not supplied the {@link #INSTALL_ALLOW_TEST} flag.
*
* @hide
*/
@@ -1032,9 +1023,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_TEST_ONLY = -15;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package being installed contains
- * native code, but none that is compatible with the device's CPU_ABI.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package being installed contains native code, but none that is compatible with the
+ * device's CPU_ABI.
*
* @hide
*/
@@ -1042,9 +1033,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a feature that is
- * not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a feature that is not available.
*
* @hide
*/
@@ -1053,9 +1043,9 @@ public abstract class PackageManager {
// ------ Errors related to sdcard
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a secure container mount point
- * couldn't be accessed on external media.
+ * Installation return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if a secure container mount point couldn't be
+ * accessed on external media.
*
* @hide
*/
@@ -1063,9 +1053,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * in the specified install location.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed in the specified install location.
*
* @hide
*/
@@ -1073,9 +1062,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * in the specified install location because the media is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed in the specified install location because the media
+ * is not available.
*
* @hide
*/
@@ -1083,9 +1072,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * because the verification timed out.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed because the verification timed out.
*
* @hide
*/
@@ -1093,9 +1081,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * because the verification did not succeed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed because the verification did not succeed.
*
* @hide
*/
@@ -1103,9 +1090,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package changed from what the
- * calling program expected.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package changed from what the calling program expected.
*
* @hide
*/
@@ -1113,28 +1099,25 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package is assigned a
- * different UID than it previously held.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package is assigned a different UID than it previously held.
*
* @hide
*/
public static final int INSTALL_FAILED_UID_CHANGED = -24;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package has an older version
- * code than the currently installed package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package has an older version code than the currently installed package.
*
* @hide
*/
public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the old package has target SDK high
- * enough to support runtime permission and the new package has target SDK
- * low enough to not support runtime permissions.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the old package has target SDK high enough to support runtime permission and the new
+ * package has target SDK low enough to not support runtime permissions.
*
* @hide
*/
@@ -1142,9 +1125,8 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package attempts to downgrade the
- * target sandbox version of the app.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package attempts to downgrade the target sandbox version of the app.
*
* @hide
*/
@@ -1152,9 +1134,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE = -27;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser was given a path that is
- * not a file, or does not end with the expected '.apk' extension.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was given a path that is not a
+ * file, or does not end with the expected '.apk' extension.
*
* @hide
*/
@@ -1162,8 +1144,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser was unable to retrieve the
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was unable to retrieve the
* AndroidManifest.xml file.
*
* @hide
@@ -1172,8 +1154,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered an unexpected
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered an unexpected
* exception.
*
* @hide
@@ -1182,9 +1164,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser did not find any
- * certificates in the .apk.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any certificates in
+ * the .apk.
*
* @hide
*/
@@ -1192,9 +1174,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser found inconsistent
- * certificates on the files in the .apk.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser found inconsistent certificates on
+ * the files in the .apk.
*
* @hide
*/
@@ -1202,8 +1184,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a
* CertificateEncodingException in one of the files in the .apk.
*
* @hide
@@ -1212,9 +1194,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a bad or
- * missing package name in the manifest.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad or missing
+ * package name in the manifest.
*
* @hide
*/
@@ -1222,9 +1204,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a bad shared
- * user id name in the manifest.
+ * Installation parse return code: tthis is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad shared user id
+ * name in the manifest.
*
* @hide
*/
@@ -1232,8 +1214,8 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered some structural
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered some structural
* problem in the manifest.
*
* @hide
@@ -1242,9 +1224,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser did not find any actionable
- * tags (instrumentation or application) in the manifest.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any actionable tags
+ * (instrumentation or application) in the manifest.
*
* @hide
*/
@@ -1252,9 +1234,9 @@ public abstract class PackageManager {
public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because of system issues.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because of system issues.
*
* @hide
*/
@@ -1262,24 +1244,23 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because the user is restricted from installing apps.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because the user is restricted from installing apps.
*
* @hide
*/
public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because it is attempting to define a permission that is already
- * defined by some existing package.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because it is attempting to define a permission that is already defined by some existing
+ * package.
* <p>
- * The package name of the app which has already defined the permission is
- * passed to a {@link PackageInstallObserver}, if any, as the
- * {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string extra; and the name of the
- * permission being redefined is passed in the
+ * The package name of the app which has already defined the permission is passed to a
+ * {@link PackageInstallObserver}, if any, as the {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string
+ * extra; and the name of the permission being redefined is passed in the
* {@link #EXTRA_FAILURE_EXISTING_PERMISSION} string extra.
*
* @hide
@@ -1287,10 +1268,9 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because its packaged native code did not match any of the ABIs
- * supported by the system.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because its packaged native code did not match any of the ABIs supported by the system.
*
* @hide
*/
@@ -4718,16 +4698,6 @@ public abstract class PackageManager {
@Deprecated
public abstract void installPackage(
Uri packageURI,
- IPackageInstallObserver observer,
- @InstallFlags int flags,
- String installerPackageName);
- /**
- * @deprecated replaced by {@link PackageInstaller}
- * @hide
- */
- @Deprecated
- public abstract void installPackage(
- Uri packageURI,
PackageInstallObserver observer,
@InstallFlags int flags,
String installerPackageName);
@@ -5743,25 +5713,6 @@ public abstract class PackageManager {
}
/** {@hide} */
- public static class LegacyPackageInstallObserver extends PackageInstallObserver {
- private final IPackageInstallObserver mLegacy;
-
- public LegacyPackageInstallObserver(IPackageInstallObserver legacy) {
- mLegacy = legacy;
- }
-
- @Override
- public void onPackageInstalled(String basePackageName, int returnCode, String msg,
- Bundle extras) {
- if (mLegacy == null) return;
- try {
- mLegacy.packageInstalled(basePackageName, returnCode);
- } catch (RemoteException ignored) {
- }
- }
- }
-
- /** {@hide} */
public static class LegacyPackageDeleteObserver extends PackageDeleteObserver {
private final IPackageDeleteObserver mLegacy;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 46ad3f0eccc9..3a3048ef1de2 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1280,11 +1280,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <ul>
* <li>Processed (but stalling): any non-RAW format with a stallDurations &gt; 0.
* Typically {@link android.graphics.ImageFormat#JPEG JPEG format}.</li>
- * <li>Raw formats: {@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}, {@link android.graphics.ImageFormat#RAW10 RAW10}, or {@link android.graphics.ImageFormat#RAW12 RAW12}.</li>
- * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
- * Typically {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888},
- * {@link android.graphics.ImageFormat#NV21 NV21}, or
- * {@link android.graphics.ImageFormat#YV12 YV12}.</li>
+ * <li>Raw formats: {@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}, {@link android.graphics.ImageFormat#RAW10 RAW10}, or
+ * {@link android.graphics.ImageFormat#RAW12 RAW12}.</li>
+ * <li>Processed (but not-stalling): any non-RAW format without a stall duration. Typically
+ * {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888},
+ * {@link android.graphics.ImageFormat#NV21 NV21}, or {@link android.graphics.ImageFormat#YV12 YV12}.</li>
* </ul>
* <p><b>Range of valid values:</b><br></p>
* <p>For processed (and stalling) format streams, &gt;= 1.</p>
@@ -1376,8 +1376,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* CPU resources that will consume more power. The image format for this kind of an output stream can
* be any non-<code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
* <p>A processed and stalling format is defined as any non-RAW format with a stallDurations
- * &gt; 0. Typically only the {@link android.graphics.ImageFormat#JPEG JPEG format} is a
- * stalling format.</p>
+ * &gt; 0. Typically only the {@link android.graphics.ImageFormat#JPEG JPEG format} is a stalling format.</p>
* <p>For full guarantees, query {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } with a
* processed format -- it will return a non-0 value for a stalling stream.</p>
* <p>LEGACY devices will support up to 1 processing/stalling stream.</p>
@@ -1535,8 +1534,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<int[]>("android.request.availableRequestKeys", int[].class);
/**
- * <p>A list of all keys that the camera device has available
- * to use with {@link android.hardware.camera2.CaptureResult }.</p>
+ * <p>A list of all keys that the camera device has available to use with {@link android.hardware.camera2.CaptureResult }.</p>
* <p>Attempting to get a key from a CaptureResult that is not
* listed here will always return a <code>null</code> value. Getting a key from
* a CaptureResult that is listed here will generally never return a <code>null</code>
@@ -1561,8 +1559,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<int[]>("android.request.availableResultKeys", int[].class);
/**
- * <p>A list of all keys that the camera device has available
- * to use with {@link android.hardware.camera2.CameraCharacteristics }.</p>
+ * <p>A list of all keys that the camera device has available to use with {@link android.hardware.camera2.CameraCharacteristics }.</p>
* <p>This entry follows the same rules as
* android.request.availableResultKeys (except that it applies for
* CameraCharacteristics instead of CaptureResult). See above for more
@@ -1843,8 +1840,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
* android.scaler.availableStallDurations for more details about
* calculating the max frame rate.</p>
- * <p>(Keep in sync with
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration })</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p>This key is available on all devices.</p>
*
@@ -1905,14 +1900,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <ul>
* <li>{@link android.graphics.ImageFormat#YUV_420_888 }</li>
* <li>{@link android.graphics.ImageFormat#RAW10 }</li>
+ * <li>{@link android.graphics.ImageFormat#RAW12 }</li>
* </ul>
* <p>All other formats may or may not have an allowed stall duration on
* a per-capability basis; refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
* for more details.</p>
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} for more information about
* calculating the max frame rate (absent stalls).</p>
- * <p>(Keep up to date with
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } )</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p>This key is available on all devices.</p>
*
@@ -2195,9 +2189,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* the raw buffers produced by this sensor.</p>
* <p>If a camera device supports raw sensor formats, either this or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} is the maximum dimensions for the raw
- * output formats listed in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} (this depends on
- * whether or not the image sensor returns buffers containing pixels that are not
- * part of the active array region for blacklevel calibration or other purposes).</p>
+ * output formats listed in {@link android.hardware.camera2.params.StreamConfigurationMap }
+ * (this depends on whether or not the image sensor returns buffers containing pixels that
+ * are not part of the active array region for blacklevel calibration or other purposes).</p>
* <p>Some parts of the full pixel array may not receive light from the scene,
* or be otherwise inactive. The {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} key
* defines the rectangle of active pixels that will be included in processed image
@@ -2205,7 +2199,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p><b>Units</b>: Pixels</p>
* <p>This key is available on all devices.</p>
*
- * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
* @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@@ -2838,7 +2831,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>See the individual level enums for full descriptions of the supported capabilities. The
* {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a
* finer-grain level, if needed. In addition, many controls have their available settings or
- * ranges defined in individual {@link android.hardware.camera2.CameraCharacteristics } entries.</p>
+ * ranges defined in individual entries from {@link android.hardware.camera2.CameraCharacteristics }.</p>
* <p>Some features are not part of any particular hardware level or capability and must be
* queried separately. These include:</p>
* <ul>
@@ -2973,7 +2966,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
* android.scaler.availableStallDurations for more details about
* calculating the max frame rate.</p>
- * <p>(Keep in sync with {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration })</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 8c8c49fa6373..4b57018bfddf 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -587,8 +587,8 @@ public abstract class CameraMetadata<TKey> {
* then the list of resolutions for YUV_420_888 from {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } contains at
* least one resolution &gt;= 8 megapixels, with a minimum frame duration of &lt;= 1/20
* s.</p>
- * <p>If the device supports the {@link android.graphics.ImageFormat#RAW10 }, {@link android.graphics.ImageFormat#RAW12 }, then those can also be captured at the same rate
- * as the maximum-size YUV_420_888 resolution is.</p>
+ * <p>If the device supports the {@link android.graphics.ImageFormat#RAW10 }, {@link android.graphics.ImageFormat#RAW12 }, then those can also be
+ * captured at the same rate as the maximum-size YUV_420_888 resolution is.</p>
* <p>If the device supports the PRIVATE_REPROCESSING capability, then the same guarantees
* as for the YUV_420_888 format also apply to the {@link android.graphics.ImageFormat#PRIVATE } format.</p>
* <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is guaranted to have a value between 0
@@ -610,25 +610,22 @@ public abstract class CameraMetadata<TKey> {
* following:</p>
* <ul>
* <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
- * <li>{@link android.graphics.ImageFormat#YUV_420_888 } is supported as an output/input format, that is,
- * YUV_420_888 is included in the lists of formats returned by
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
+ * <li>{@link android.graphics.ImageFormat#YUV_420_888 } is supported as an output/input
+ * format, that is, YUV_420_888 is included in the lists of formats returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
* <li>{@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
* returns non-empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
* <li>Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(YUV_420_888)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(YUV_420_888)}</li>
- * <li>Using {@link android.graphics.ImageFormat#YUV_420_888 } does not cause a frame rate drop
- * relative to the sensor's maximum capture rate (at that resolution).</li>
+ * <li>Using {@link android.graphics.ImageFormat#YUV_420_888 } does not cause a frame rate
+ * drop relative to the sensor's maximum capture rate (at that resolution).</li>
* <li>{@link android.graphics.ImageFormat#YUV_420_888 } will be reprocessable into both
* {@link android.graphics.ImageFormat#YUV_420_888 } and {@link android.graphics.ImageFormat#JPEG } formats.</li>
* <li>The maximum available resolution for {@link android.graphics.ImageFormat#YUV_420_888 } streams (both input/output) will match the
* maximum available resolution of {@link android.graphics.ImageFormat#JPEG } streams.</li>
* <li>Static metadata {@link CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL android.reprocess.maxCaptureStall}.</li>
* <li>Only the below controls are effective for reprocessing requests and will be present
- * in capture results. The reprocess requests are from the original capture results that
- * are associated with the intermediate {@link android.graphics.ImageFormat#YUV_420_888 }
- * output buffers. All other controls in the reprocess requests will be ignored by the
- * camera device.<ul>
+ * in capture results. The reprocess requests are from the original capture results
+ * that are associated with the intermediate {@link android.graphics.ImageFormat#YUV_420_888 } output buffers. All other controls in the
+ * reprocess requests will be ignored by the camera device.<ul>
* <li>android.jpeg.*</li>
* <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
* <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
@@ -654,13 +651,13 @@ public abstract class CameraMetadata<TKey> {
* <p>The camera device can produce depth measurements from its field of view.</p>
* <p>This capability requires the camera device to support the following:</p>
* <ul>
- * <li>{@link android.graphics.ImageFormat#DEPTH16 } is supported as an output format.</li>
- * <li>{@link android.graphics.ImageFormat#DEPTH_POINT_CLOUD } is optionally supported as an
- * output format.</li>
- * <li>This camera device, and all camera devices with the same {@link CameraCharacteristics#LENS_FACING android.lens.facing},
- * will list the following calibration entries in both
- * {@link android.hardware.camera2.CameraCharacteristics } and
- * {@link android.hardware.camera2.CaptureResult }:<ul>
+ * <li>{@link android.graphics.ImageFormat#DEPTH16 } is supported as
+ * an output format.</li>
+ * <li>{@link android.graphics.ImageFormat#DEPTH_POINT_CLOUD } is
+ * optionally supported as an output format.</li>
+ * <li>This camera device, and all camera devices with the same {@link CameraCharacteristics#LENS_FACING android.lens.facing}, will
+ * list the following calibration metadata entries in both {@link android.hardware.camera2.CameraCharacteristics }
+ * and {@link android.hardware.camera2.CaptureResult }:<ul>
* <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
* <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
* <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
@@ -674,8 +671,7 @@ public abstract class CameraMetadata<TKey> {
* </ul>
* <p>Generally, depth output operates at a slower frame rate than standard color capture,
* so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that
- * should be accounted for (see
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }).
+ * should be accounted for (see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }).
* On a device that supports both depth and color-based output, to enable smooth preview,
* using a repeating burst is recommended, where a depth-output target is only included
* once every N frames, where N is the ratio between preview output rate and depth output
@@ -692,23 +688,19 @@ public abstract class CameraMetadata<TKey> {
public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
/**
- * <p>The device supports constrained high speed video recording (frame rate &gt;=120fps)
- * use case. The camera device will support high speed capture session created by
- * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which
- * only accepts high speed request lists created by
- * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }.</p>
- * <p>A camera device can still support high speed video streaming by advertising the high speed
- * FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all normal
- * capture request per frame control and synchronization requirements will apply to
- * the high speed fps ranges, the same as all other fps ranges. This capability describes
- * the capability of a specialized operating mode with many limitations (see below), which
- * is only targeted at high speed video recording.</p>
- * <p>The supported high speed video sizes and fps ranges are specified in
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.
- * To get desired output frame rates, the application is only allowed to select video size
- * and FPS range combinations provided by
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.
- * The fps range can be controlled via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
+ * <p>The device supports constrained high speed video recording (frame rate &gt;=120fps) use
+ * case. The camera device will support high speed capture session created by {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which
+ * only accepts high speed request lists created by {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }.</p>
+ * <p>A camera device can still support high speed video streaming by advertising the high
+ * speed FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all
+ * normal capture request per frame control and synchronization requirements will apply
+ * to the high speed fps ranges, the same as all other fps ranges. This capability
+ * describes the capability of a specialized operating mode with many limitations (see
+ * below), which is only targeted at high speed video recording.</p>
+ * <p>The supported high speed video sizes and fps ranges are specified in {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.
+ * To get desired output frame rates, the application is only allowed to select video
+ * size and FPS range combinations provided by {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }. The
+ * fps range can be controlled via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
* <p>In this capability, the camera device will override aeMode, awbMode, and afMode to
* ON, AUTO, and CONTINUOUS_VIDEO, respectively. All post-processing block mode
* controls will be overridden to be FAST. Therefore, no manual control of capture
@@ -743,19 +735,16 @@ public abstract class CameraMetadata<TKey> {
* frame rate. If the destination surface is from preview window, the actual preview frame
* rate will be bounded by the screen refresh rate.</p>
* <p>The camera device will only support up to 2 high speed simultaneous output surfaces
- * (preview and recording surfaces)
- * in this mode. Above controls will be effective only if all of below conditions are true:</p>
+ * (preview and recording surfaces) in this mode. Above controls will be effective only
+ * if all of below conditions are true:</p>
* <ul>
* <li>The application creates a camera capture session with no more than 2 surfaces via
* {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }. The
- * targeted surfaces must be preview surface (either from
- * {@link android.view.SurfaceView } or {@link android.graphics.SurfaceTexture }) or
- * recording surface(either from {@link android.media.MediaRecorder#getSurface } or
- * {@link android.media.MediaCodec#createInputSurface }).</li>
+ * targeted surfaces must be preview surface (either from {@link android.view.SurfaceView } or {@link android.graphics.SurfaceTexture }) or recording
+ * surface(either from {@link android.media.MediaRecorder#getSurface } or {@link android.media.MediaCodec#createInputSurface }).</li>
* <li>The stream sizes are selected from the sizes reported by
* {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.</li>
- * <li>The FPS ranges are selected from
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
+ * <li>The FPS ranges are selected from {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
* </ul>
* <p>When above conditions are NOT satistied,
* {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
@@ -1038,8 +1027,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>This camera device is running in backward compatibility mode.</p>
- * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
- * documentation are supported.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are supported.</p>
* <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual
* post-processing, arbitrary cropping regions, and has relaxed performance constraints.
* No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a
@@ -1061,8 +1049,7 @@ public abstract class CameraMetadata<TKey> {
* <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to
* FULL-level capabilities.</p>
* <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and
- * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
- * documentation are guaranteed to be supported.</p>
+ * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p>
* <p>The following additional capabilities are guaranteed to be supported:</p>
* <ul>
* <li><code>YUV_REPROCESSING</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
@@ -2155,12 +2142,13 @@ public abstract class CameraMetadata<TKey> {
public static final int EDGE_MODE_HIGH_QUALITY = 2;
/**
- * <p>Edge enhancement is applied at different levels for different output streams,
- * based on resolution. Streams at maximum recording resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession }) or below have
- * edge enhancement applied, while higher-resolution streams have no edge enhancement
- * applied. The level of edge enhancement for low-resolution streams is tuned so that
- * frame rate is not impacted, and the quality is equal to or better than FAST (since it
- * is only applied to lower-resolution outputs, quality may improve from FAST).</p>
+ * <p>Edge enhancement is applied at different
+ * levels for different output streams, based on resolution. Streams at maximum recording
+ * resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession })
+ * or below have edge enhancement applied, while higher-resolution streams have no edge
+ * enhancement applied. The level of edge enhancement for low-resolution streams is tuned
+ * so that frame rate is not impacted, and the quality is equal to or better than FAST
+ * (since it is only applied to lower-resolution outputs, quality may improve from FAST).</p>
* <p>This mode is intended to be used by applications operating in a zero-shutter-lag mode
* with YUV or PRIVATE reprocessing, where the application continuously captures
* high-resolution intermediate buffers into a circular buffer, from which a final image is
@@ -2287,12 +2275,12 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>Noise reduction is applied at different levels for different output streams,
- * based on resolution. Streams at maximum recording resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession }) or below have noise
- * reduction applied, while higher-resolution streams have MINIMAL (if supported) or no
- * noise reduction applied (if MINIMAL is not supported.) The degree of noise reduction
- * for low-resolution streams is tuned so that frame rate is not impacted, and the quality
- * is equal to or better than FAST (since it is only applied to lower-resolution outputs,
- * quality may improve from FAST).</p>
+ * based on resolution. Streams at maximum recording resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession })
+ * or below have noise reduction applied, while higher-resolution streams have MINIMAL (if
+ * supported) or no noise reduction applied (if MINIMAL is not supported.) The degree of
+ * noise reduction for low-resolution streams is tuned so that frame rate is not impacted,
+ * and the quality is equal to or better than FAST (since it is only applied to
+ * lower-resolution outputs, quality may improve from FAST).</p>
* <p>This mode is intended to be used by applications operating in a zero-shutter-lag mode
* with YUV or PRIVATE reprocessing, where the application continuously captures
* high-resolution intermediate buffers into a circular buffer, from which a final image is
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c41fc0207d92..0262ecb54f0d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -680,7 +680,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* FAST or HIGH_QUALITY will yield a picture with the same white point
* as what was produced by the camera device in the earlier frame.</p>
* <p>The expected processing pipeline is as follows:</p>
- * <p><img alt="White balance processing pipeline" src="../../../../images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
+ * <p><img alt="White balance processing pipeline" src="/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
* <p>The white balance is encoded by two values, a 4-channel white-balance
* gain vector (applied in the Bayer domain), and a 3x3 color transform
* matrix (applied after demosaic).</p>
@@ -1470,10 +1470,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p>When set to AUTO, the individual algorithm controls in
* android.control.* are in effect, such as {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}.</p>
* <p>When set to USE_SCENE_MODE, the individual controls in
- * android.control.* are mostly disabled, and the camera device implements
- * one of the scene mode settings (such as ACTION, SUNSET, or PARTY)
- * as it wishes. The camera device scene mode 3A settings are provided by
- * {@link android.hardware.camera2.CaptureResult capture results}.</p>
+ * android.control.* are mostly disabled, and the camera device
+ * implements one of the scene mode settings (such as ACTION,
+ * SUNSET, or PARTY) as it wishes. The camera device scene mode
+ * 3A settings are provided by {@link android.hardware.camera2.CaptureResult capture results}.</p>
* <p>When set to OFF_KEEP_STATE, it is similar to OFF mode, the only difference
* is that this frame will not be used by camera device background 3A statistics
* update, as if this frame is never captured. This mode can be used in the scenario
@@ -2268,45 +2268,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* can run concurrently to the rest of the camera pipeline, but
* cannot process more than 1 capture at a time.</li>
* </ul>
- * <p>The necessary information for the application, given the model above,
- * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field using
+ * <p>The necessary information for the application, given the model above, is provided via
* {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }.
- * These are used to determine the maximum frame rate / minimum frame
- * duration that is possible for a given stream configuration.</p>
+ * These are used to determine the maximum frame rate / minimum frame duration that is
+ * possible for a given stream configuration.</p>
* <p>Specifically, the application can use the following rules to
* determine the minimum frame duration it can request from the camera
* device:</p>
* <ol>
- * <li>Let the set of currently configured input/output streams
- * be called <code>S</code>.</li>
- * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking
- * it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
- * (with its respective size/format). Let this set of frame durations be
- * called <code>F</code>.</li>
- * <li>For any given request <code>R</code>, the minimum frame duration allowed
- * for <code>R</code> is the maximum out of all values in <code>F</code>. Let the streams
- * used in <code>R</code> be called <code>S_r</code>.</li>
+ * <li>Let the set of currently configured input/output streams be called <code>S</code>.</li>
+ * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking it up in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
+ * (with its respective size/format). Let this set of frame durations be called <code>F</code>.</li>
+ * <li>For any given request <code>R</code>, the minimum frame duration allowed for <code>R</code> is the maximum
+ * out of all values in <code>F</code>. Let the streams used in <code>R</code> be called <code>S_r</code>.</li>
* </ol>
* <p>If none of the streams in <code>S_r</code> have a stall time (listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }
- * using its respective size/format), then the frame duration in <code>F</code>
- * determines the steady state frame rate that the application will get
- * if it uses <code>R</code> as a repeating request. Let this special kind of
- * request be called <code>Rsimple</code>.</p>
- * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved
- * by a single capture of a new request <code>Rstall</code> (which has at least
- * one in-use stream with a non-0 stall time) and if <code>Rstall</code> has the
- * same minimum frame duration this will not cause a frame rate loss
- * if all buffers from the previous <code>Rstall</code> have already been
- * delivered.</p>
- * <p>For more details about stalling, see
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
+ * using its respective size/format), then the frame duration in <code>F</code> determines the steady
+ * state frame rate that the application will get if it uses <code>R</code> as a repeating request. Let
+ * this special kind of request be called <code>Rsimple</code>.</p>
+ * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved by a single capture of a
+ * new request <code>Rstall</code> (which has at least one in-use stream with a non-0 stall time) and if
+ * <code>Rstall</code> has the same minimum frame duration this will not cause a frame rate loss if all
+ * buffers from the previous <code>Rstall</code> have already been delivered.</p>
+ * <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
- * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration},
- * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}. The duration
- * is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
+ * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
+ * The duration is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -2315,7 +2305,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
* @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
*/
@PublicKey
@@ -2584,11 +2573,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p>Linear mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
@@ -2596,7 +2585,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
* 0.8000, 0.9035, 0.8667, 0.9370, 0.9333, 0.9691, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
@@ -2604,7 +2593,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
* 0.8000, 0.9063, 0.8667, 0.9389, 0.9333, 0.9701, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Range of valid values:</b><br>
* 0-1 on both input and output coordinates, normalized
* as a floating-point value such that 0 == black and 1 == white.</p>
@@ -2646,11 +2635,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p>Linear mapping:</p>
* <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
@@ -2658,7 +2647,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
* (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
@@ -2666,7 +2655,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
* (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -2756,9 +2745,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* PRESET_CURVE</p>
* <p>The tonemap curve will be defined by specified standard.</p>
* <p>sRGB (approximated by 16 control points):</p>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p>Rec. 709 (approximated by 16 control points):</p>
- * <p><img alt="Rec. 709 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
+ * <p><img alt="Rec. 709 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
* <p>Note that above figures show a 16 control points approximation of preset
* curves. Camera devices may apply a different approximation to the curve.</p>
* <p><b>Possible values:</b>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6d80c20a84af..cfad098c0cd2 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -390,7 +390,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* FAST or HIGH_QUALITY will yield a picture with the same white point
* as what was produced by the camera device in the earlier frame.</p>
* <p>The expected processing pipeline is as follows:</p>
- * <p><img alt="White balance processing pipeline" src="../../../../images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
+ * <p><img alt="White balance processing pipeline" src="/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
* <p>The white balance is encoded by two values, a 4-channel white-balance
* gain vector (applied in the Bayer domain), and a 3x3 color transform
* matrix (applied after demosaic).</p>
@@ -1975,10 +1975,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>When set to AUTO, the individual algorithm controls in
* android.control.* are in effect, such as {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}.</p>
* <p>When set to USE_SCENE_MODE, the individual controls in
- * android.control.* are mostly disabled, and the camera device implements
- * one of the scene mode settings (such as ACTION, SUNSET, or PARTY)
- * as it wishes. The camera device scene mode 3A settings are provided by
- * {@link android.hardware.camera2.CaptureResult capture results}.</p>
+ * android.control.* are mostly disabled, and the camera device
+ * implements one of the scene mode settings (such as ACTION,
+ * SUNSET, or PARTY) as it wishes. The camera device scene mode
+ * 3A settings are provided by {@link android.hardware.camera2.CaptureResult capture results}.</p>
* <p>When set to OFF_KEEP_STATE, it is similar to OFF mode, the only difference
* is that this frame will not be used by camera device background 3A statistics
* update, as if this frame is never captured. This mode can be used in the scenario
@@ -3108,45 +3108,35 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* can run concurrently to the rest of the camera pipeline, but
* cannot process more than 1 capture at a time.</li>
* </ul>
- * <p>The necessary information for the application, given the model above,
- * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field using
+ * <p>The necessary information for the application, given the model above, is provided via
* {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }.
- * These are used to determine the maximum frame rate / minimum frame
- * duration that is possible for a given stream configuration.</p>
+ * These are used to determine the maximum frame rate / minimum frame duration that is
+ * possible for a given stream configuration.</p>
* <p>Specifically, the application can use the following rules to
* determine the minimum frame duration it can request from the camera
* device:</p>
* <ol>
- * <li>Let the set of currently configured input/output streams
- * be called <code>S</code>.</li>
- * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking
- * it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
- * (with its respective size/format). Let this set of frame durations be
- * called <code>F</code>.</li>
- * <li>For any given request <code>R</code>, the minimum frame duration allowed
- * for <code>R</code> is the maximum out of all values in <code>F</code>. Let the streams
- * used in <code>R</code> be called <code>S_r</code>.</li>
+ * <li>Let the set of currently configured input/output streams be called <code>S</code>.</li>
+ * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking it up in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
+ * (with its respective size/format). Let this set of frame durations be called <code>F</code>.</li>
+ * <li>For any given request <code>R</code>, the minimum frame duration allowed for <code>R</code> is the maximum
+ * out of all values in <code>F</code>. Let the streams used in <code>R</code> be called <code>S_r</code>.</li>
* </ol>
* <p>If none of the streams in <code>S_r</code> have a stall time (listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }
- * using its respective size/format), then the frame duration in <code>F</code>
- * determines the steady state frame rate that the application will get
- * if it uses <code>R</code> as a repeating request. Let this special kind of
- * request be called <code>Rsimple</code>.</p>
- * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved
- * by a single capture of a new request <code>Rstall</code> (which has at least
- * one in-use stream with a non-0 stall time) and if <code>Rstall</code> has the
- * same minimum frame duration this will not cause a frame rate loss
- * if all buffers from the previous <code>Rstall</code> have already been
- * delivered.</p>
- * <p>For more details about stalling, see
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
+ * using its respective size/format), then the frame duration in <code>F</code> determines the steady
+ * state frame rate that the application will get if it uses <code>R</code> as a repeating request. Let
+ * this special kind of request be called <code>Rsimple</code>.</p>
+ * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved by a single capture of a
+ * new request <code>Rstall</code> (which has at least one in-use stream with a non-0 stall time) and if
+ * <code>Rstall</code> has the same minimum frame duration this will not cause a frame rate loss if all
+ * buffers from the previous <code>Rstall</code> have already been delivered.</p>
+ * <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
- * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration},
- * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}. The duration
- * is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
+ * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
+ * The duration is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -3155,7 +3145,6 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
* @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
*/
@PublicKey
@@ -3408,9 +3397,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* layout key (see {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}), i.e. the
* nth value given corresponds to the black level offset for the nth
* color channel listed in the CFA.</p>
- * <p>This key will be available if {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} is
- * available or the camera device advertises this key via
- * {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
+ * <p>This key will be available if {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} is available or the
+ * camera device advertises this key via {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
* <p><b>Range of valid values:</b><br>
* &gt;= 0 for each.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3640,13 +3628,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* </code></pre>
* <p>The low-resolution scaling map images for each channel are
* (displayed using nearest-neighbor interpolation):</p>
- * <p><img alt="Red lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
- * <img alt="Green (even rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
- * <img alt="Green (odd rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
- * <img alt="Blue lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
+ * <p><img alt="Red lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
+ * <img alt="Green (even rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
+ * <img alt="Green (odd rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
+ * <img alt="Blue lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
* <p>As a visualization only, inverting the full-color map to recover an
* image of a gray wall (using bicubic interpolation for visual quality) as captured by the sensor gives:</p>
- * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+ * <p><img alt="Image of a uniform white wall (inverse shading map)" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
* <p><b>Range of valid values:</b><br>
* Each gain factor is &gt;= 1</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3707,14 +3695,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* </code></pre>
* <p>The low-resolution scaling map images for each channel are
* (displayed using nearest-neighbor interpolation):</p>
- * <p><img alt="Red lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
- * <img alt="Green (even rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
- * <img alt="Green (odd rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
- * <img alt="Blue lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
+ * <p><img alt="Red lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
+ * <img alt="Green (even rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
+ * <img alt="Green (odd rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
+ * <img alt="Blue lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
* <p>As a visualization only, inverting the full-color map to recover an
* image of a gray wall (using bicubic interpolation for visual quality)
* as captured by the sensor gives:</p>
- * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+ * <p><img alt="Image of a uniform white wall (inverse shading map)" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
* <p>Note that the RAW image data might be subject to lens shading
* correction not reported on this map. Query
* {@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied} to see if RAW image data has subject
@@ -3947,11 +3935,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>Linear mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
@@ -3959,7 +3947,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
* 0.8000, 0.9035, 0.8667, 0.9370, 0.9333, 0.9691, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
@@ -3967,7 +3955,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
* 0.8000, 0.9063, 0.8667, 0.9389, 0.9333, 0.9701, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Range of valid values:</b><br>
* 0-1 on both input and output coordinates, normalized
* as a floating-point value such that 0 == black and 1 == white.</p>
@@ -4009,11 +3997,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>Linear mapping:</p>
* <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
@@ -4021,7 +4009,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
* (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
@@ -4029,7 +4017,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
* (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -4119,9 +4107,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* PRESET_CURVE</p>
* <p>The tonemap curve will be defined by specified standard.</p>
* <p>sRGB (approximated by 16 control points):</p>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p>Rec. 709 (approximated by 16 control points):</p>
- * <p><img alt="Rec. 709 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
+ * <p><img alt="Rec. 709 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
* <p>Note that above figures show a 16 control points approximation of preset
* curves. Camera devices may apply a different approximation to the curve.</p>
* <p><b>Possible values:</b>
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index aaf6c57b9abe..e1137aa51348 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -16,6 +16,7 @@
package android.hardware.location;
import android.annotation.SystemApi;
+import android.hardware.contexthub.V1_0.ContextHub;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,22 +32,52 @@ public class ContextHubInfo {
private String mVendor;
private String mToolchain;
private int mPlatformVersion;
- private int mStaticSwVersion;
private int mToolchainVersion;
private float mPeakMips;
private float mStoppedPowerDrawMw;
private float mSleepPowerDrawMw;
private float mPeakPowerDrawMw;
private int mMaxPacketLengthBytes;
+ private byte mChreApiMajorVersion;
+ private byte mChreApiMinorVersion;
+ private short mChrePatchVersion;
+ private long mChrePlatformId;
private int[] mSupportedSensors;
private MemoryRegion[] mMemoryRegions;
+ /*
+ * TODO(b/67734082): Deprecate this constructor and mark private fields as final.
+ */
public ContextHubInfo() {
}
/**
+ * @hide
+ */
+ public ContextHubInfo(ContextHub contextHub) {
+ mId = contextHub.hubId;
+ mName = contextHub.name;
+ mVendor = contextHub.vendor;
+ mToolchain = contextHub.toolchain;
+ mPlatformVersion = contextHub.platformVersion;
+ mToolchainVersion = contextHub.toolchainVersion;
+ mPeakMips = contextHub.peakMips;
+ mStoppedPowerDrawMw = contextHub.stoppedPowerDrawMw;
+ mSleepPowerDrawMw = contextHub.sleepPowerDrawMw;
+ mPeakPowerDrawMw = contextHub.peakPowerDrawMw;
+ mMaxPacketLengthBytes = contextHub.maxSupportedMsgLen;
+ mChrePlatformId = contextHub.chrePlatformId;
+ mChreApiMajorVersion = contextHub.chreApiMajorVersion;
+ mChreApiMinorVersion = contextHub.chreApiMinorVersion;
+ mChrePatchVersion = contextHub.chrePatchVersion;
+
+ mSupportedSensors = new int[0];
+ mMemoryRegions = new MemoryRegion[0];
+ }
+
+ /**
* returns the maximum number of bytes that can be sent per message to the hub
*
* @return int - maximum bytes that can be transmitted in a
@@ -57,17 +88,6 @@ public class ContextHubInfo {
}
/**
- * set the context hub unique identifer
- *
- * @param bytes - Maximum number of bytes per message
- *
- * @hide
- */
- public void setMaxPacketLenBytes(int bytes) {
- mMaxPacketLengthBytes = bytes;
- }
-
- /**
* get the context hub unique identifer
*
* @return int - unique system wide identifier
@@ -77,17 +97,6 @@ public class ContextHubInfo {
}
/**
- * set the context hub unique identifer
- *
- * @param id - unique system wide identifier for the hub
- *
- * @hide
- */
- public void setId(int id) {
- mId = id;
- }
-
- /**
* get a string as a hub name
*
* @return String - a name for the hub
@@ -97,17 +106,6 @@ public class ContextHubInfo {
}
/**
- * set a string as the hub name
- *
- * @param name - the name for the hub
- *
- * @hide
- */
- public void setName(String name) {
- mName = name;
- }
-
- /**
* get a string as the vendor name
*
* @return String - a name for the vendor
@@ -117,17 +115,6 @@ public class ContextHubInfo {
}
/**
- * set a string as the vendor name
- *
- * @param vendor - a name for the vendor
- *
- * @hide
- */
- public void setVendor(String vendor) {
- mVendor = vendor;
- }
-
- /**
* get tool chain string
*
* @return String - description of the tool chain
@@ -137,17 +124,6 @@ public class ContextHubInfo {
}
/**
- * set tool chain string
- *
- * @param toolchain - description of the tool chain
- *
- * @hide
- */
- public void setToolchain(String toolchain) {
- mToolchain = toolchain;
- }
-
- /**
* get platform version
*
* @return int - platform version number
@@ -157,34 +133,12 @@ public class ContextHubInfo {
}
/**
- * set platform version
- *
- * @param platformVersion - platform version number
- *
- * @hide
- */
- public void setPlatformVersion(int platformVersion) {
- mPlatformVersion = platformVersion;
- }
-
- /**
* get static platform version number
*
* @return int - platform version number
*/
public int getStaticSwVersion() {
- return mStaticSwVersion;
- }
-
- /**
- * set platform software version
- *
- * @param staticSwVersion - platform static s/w version number
- *
- * @hide
- */
- public void setStaticSwVersion(int staticSwVersion) {
- mStaticSwVersion = staticSwVersion;
+ return (mChreApiMajorVersion << 24) | (mChreApiMinorVersion << 16) | (mChrePatchVersion);
}
/**
@@ -197,17 +151,6 @@ public class ContextHubInfo {
}
/**
- * set the tool chain version number
- *
- * @param toolchainVersion - tool chain version number
- *
- * @hide
- */
- public void setToolchainVersion(int toolchainVersion) {
- mToolchainVersion = toolchainVersion;
- }
-
- /**
* get the peak processing mips the hub can support
*
* @return float - peak MIPS that this hub can deliver
@@ -217,17 +160,6 @@ public class ContextHubInfo {
}
/**
- * set the peak mips that this hub can support
- *
- * @param peakMips - peak mips this hub can deliver
- *
- * @hide
- */
- public void setPeakMips(float peakMips) {
- mPeakMips = peakMips;
- }
-
- /**
* get the stopped power draw in milliwatts
* This assumes that the hub enter a stopped state - which is
* different from the sleep state. Latencies on exiting the
@@ -241,17 +173,6 @@ public class ContextHubInfo {
}
/**
- * Set the power consumed by the hub in stopped state
- *
- * @param stoppedPowerDrawMw - stopped power in milli watts
- *
- * @hide
- */
- public void setStoppedPowerDrawMw(float stoppedPowerDrawMw) {
- mStoppedPowerDrawMw = stoppedPowerDrawMw;
- }
-
- /**
* get the power draw of the hub in sleep mode. This assumes
* that the hub supports a sleep mode in which the power draw is
* lower than the power consumed when the hub is actively
@@ -267,17 +188,6 @@ public class ContextHubInfo {
}
/**
- * Set the sleep power draw in milliwatts
- *
- * @param sleepPowerDrawMw - sleep power draw in milliwatts.
- *
- * @hide
- */
- public void setSleepPowerDrawMw(float sleepPowerDrawMw) {
- mSleepPowerDrawMw = sleepPowerDrawMw;
- }
-
- /**
* get the peak powe draw of the hub. This is the power consumed
* by the hub at maximum load.
*
@@ -288,18 +198,6 @@ public class ContextHubInfo {
}
/**
- * set the peak power draw of the hub
- *
- * @param peakPowerDrawMw - peak power draw of the hub in
- * milliwatts.
- *
- * @hide
- */
- public void setPeakPowerDrawMw(float peakPowerDrawMw) {
- mPeakPowerDrawMw = peakPowerDrawMw;
- }
-
- /**
* get the sensors supported by this hub
*
* @return int[] - all the supported sensors on this hub
@@ -322,46 +220,65 @@ public class ContextHubInfo {
}
/**
- * set the supported sensors on this hub
- *
- * @param supportedSensors - supported sensors on this hub
+ * @return the CHRE platform ID as defined in chre/version.h
*
+ * TODO(b/67734082): Expose as public API
* @hide
*/
- public void setSupportedSensors(int[] supportedSensors) {
- mSupportedSensors = Arrays.copyOf(supportedSensors, supportedSensors.length);
+ public long getChrePlatformId() {
+ return mChrePlatformId;
}
/**
- * set memory regions for this hub
+ * @return the CHRE API's major version as defined in chre/version.h
*
- * @param memoryRegions - memory regions information
+ * TODO(b/67734082): Expose as public API
+ * @hide
+ */
+ public byte getChreApiMajorVersion() {
+ return mChreApiMajorVersion;
+ }
+
+ /**
+ * @return the CHRE API's minor version as defined in chre/version.h
*
- * @see MemoryRegion
+ * TODO(b/67734082): Expose as public API
+ * @hide
+ */
+ public byte getChreApiMinorVersion() {
+ return mChreApiMinorVersion;
+ }
+
+ /**
+ * @return the CHRE patch version as defined in chre/version.h
*
+ * TODO(b/67734082): Expose as public API
* @hide
*/
- public void setMemoryRegions(MemoryRegion[] memoryRegions) {
- mMemoryRegions = Arrays.copyOf(memoryRegions, memoryRegions.length);
+ public short getChrePatchVersion() {
+ return mChrePatchVersion;
}
@Override
public String toString() {
- String retVal = "";
- retVal += "Id : " + mId;
- retVal += ", Name : " + mName;
- retVal += "\n\tVendor : " + mVendor;
- retVal += ", ToolChain : " + mToolchain;
- retVal += "\n\tPlatformVersion : " + mPlatformVersion;
- retVal += ", StaticSwVersion : " + mStaticSwVersion;
- retVal += "\n\tPeakMips : " + mPeakMips;
- retVal += ", StoppedPowerDraw : " + mStoppedPowerDrawMw + " mW";
- retVal += ", PeakPowerDraw : " + mPeakPowerDrawMw + " mW";
- retVal += ", MaxPacketLength : " + mMaxPacketLengthBytes + " Bytes";
- retVal += "\n\tSupported sensors : " + Arrays.toString(mSupportedSensors);
- retVal += "\n\tMemory Regions : " + Arrays.toString(mMemoryRegions);
-
- return retVal;
+ String retVal = "";
+ retVal += "Id : " + mId;
+ retVal += ", Name : " + mName;
+ retVal += "\n\tVendor : " + mVendor;
+ retVal += ", Toolchain : " + mToolchain;
+ retVal += ", Toolchain version: 0x" + Integer.toHexString(mToolchainVersion);
+ retVal += "\n\tPlatformVersion : 0x" + Integer.toHexString(mPlatformVersion);
+ retVal += ", SwVersion : "
+ + mChreApiMajorVersion + "." + mChreApiMinorVersion + "." + mChrePatchVersion;
+ retVal += ", CHRE platform ID: 0x" + Long.toHexString(mChrePlatformId);
+ retVal += "\n\tPeakMips : " + mPeakMips;
+ retVal += ", StoppedPowerDraw : " + mStoppedPowerDrawMw + " mW";
+ retVal += ", PeakPowerDraw : " + mPeakPowerDrawMw + " mW";
+ retVal += ", MaxPacketLength : " + mMaxPacketLengthBytes + " Bytes";
+ retVal += "\n\tSupported sensors : " + Arrays.toString(mSupportedSensors);
+ retVal += "\n\tMemory Regions : " + Arrays.toString(mMemoryRegions);
+
+ return retVal;
}
private ContextHubInfo(Parcel in) {
@@ -371,12 +288,15 @@ public class ContextHubInfo {
mToolchain = in.readString();
mPlatformVersion = in.readInt();
mToolchainVersion = in.readInt();
- mStaticSwVersion = in.readInt();
mPeakMips = in.readFloat();
mStoppedPowerDrawMw = in.readFloat();
mSleepPowerDrawMw = in.readFloat();
mPeakPowerDrawMw = in.readFloat();
mMaxPacketLengthBytes = in.readInt();
+ mChrePlatformId = in.readLong();
+ mChreApiMajorVersion = in.readByte();
+ mChreApiMinorVersion = in.readByte();
+ mChrePatchVersion = (short) in.readInt();
int numSupportedSensors = in.readInt();
mSupportedSensors = new int[numSupportedSensors];
@@ -395,12 +315,15 @@ public class ContextHubInfo {
out.writeString(mToolchain);
out.writeInt(mPlatformVersion);
out.writeInt(mToolchainVersion);
- out.writeInt(mStaticSwVersion);
out.writeFloat(mPeakMips);
out.writeFloat(mStoppedPowerDrawMw);
out.writeFloat(mSleepPowerDrawMw);
out.writeFloat(mPeakPowerDrawMw);
out.writeInt(mMaxPacketLengthBytes);
+ out.writeLong(mChrePlatformId);
+ out.writeByte(mChreApiMajorVersion);
+ out.writeByte(mChreApiMinorVersion);
+ out.writeInt(mChrePatchVersion);
out.writeInt(mSupportedSensors.length);
out.writeIntArray(mSupportedSensors);
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 3aaeb5061de3..18287fae1b9b 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -94,5 +94,17 @@ interface ITuner {
*/
void setAnalogForced(boolean isForced);
+ /**
+ * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+ * @return Vendor-specific key-value pairs, must be Map<String, String>
+ */
+ Map setParameters(in Map parameters);
+
+ /**
+ * @param keys Parameter keys to fetch
+ * @return Vendor-specific key-value pairs, must be Map<String, String>
+ */
+ Map getParameters(in List<String> keys);
+
boolean isAntennaConnected();
}
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index 6ed171bbb8a9..775e25c7e7cf 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -30,4 +30,9 @@ oneway interface ITunerCallback {
void onBackgroundScanAvailabilityChange(boolean isAvailable);
void onBackgroundScanComplete();
void onProgramListChanged();
+
+ /**
+ * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+ */
+ void onParametersUpdated(in Map parameters);
}
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 6e8991aa4a4a..e93fd5f1b86b 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -309,6 +309,58 @@ public abstract class RadioTuner {
public abstract void setAnalogForced(boolean isForced);
/**
+ * Generic method for setting vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * Framework does not make any assumptions on the keys or values, other than
+ * ones stated in VendorKeyValue documentation (a requirement of key
+ * prefixes).
+ *
+ * For each pair in the result map, the key will be one of the keys
+ * contained in the input (possibly with wildcards expanded), and the value
+ * will be a vendor-specific result status (such as "OK" or an error code).
+ * The implementation may choose to return an empty map, or only return
+ * a status for a subset of the provided inputs, at its discretion.
+ *
+ * Application and HAL must not use keys with unknown prefix. In particular,
+ * it must not place a key-value pair in results vector for unknown key from
+ * parameters vector - instead, an unknown key should simply be ignored.
+ * In other words, results vector may contain a subset of parameter keys
+ * (however, the framework doesn't enforce a strict subset - the only
+ * formal requirement is vendor domain prefix for keys).
+ *
+ * @param parameters Vendor-specific key-value pairs.
+ * @return Operation completion status for parameters being set.
+ * @hide FutureFeature
+ */
+ public abstract @NonNull Map<String, String>
+ setParameters(@NonNull Map<String, String> parameters);
+
+ /**
+ * Generic method for retrieving vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * Framework does not cache set/get requests, so it's possible for
+ * getParameter to return a different value than previous setParameter call.
+ *
+ * The syntax and semantics of keys are up to the vendor (as long as prefix
+ * rules are obeyed). For instance, vendors may include some form of
+ * wildcard support. In such case, result vector may be of different size
+ * than requested keys vector. However, wildcards are not recognized by
+ * framework and they are passed as-is to the HAL implementation.
+ *
+ * Unknown keys must be ignored and not placed into results vector.
+ *
+ * @param keys Parameter keys to fetch.
+ * @return Vendor-specific key-value pairs.
+ * @hide FutureFeature
+ */
+ public abstract @NonNull Map<String, String>
+ getParameters(@NonNull List<String> keys);
+
+ /**
* Get current antenna connection state for current configuration.
* Only valid if a configuration has been applied.
* @return {@code true} if the antenna is connected, {@code false} otherwise.
@@ -429,6 +481,22 @@ public abstract class RadioTuner {
* Use {@link RadioTuner#getProgramList(String)} to get an actual list.
*/
public void onProgramListChanged() {}
+
+ /**
+ * Generic callback for passing updates to vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * It's up to the HAL implementation if and how to implement this callback,
+ * as long as it obeys the prefix rule. In particular, only selected keys
+ * may be notified this way. However, setParameters must not trigger
+ * this callback, while an internal event can change parameters
+ * asynchronously.
+ *
+ * @param parameters Vendor-specific key-value pairs.
+ * @hide FutureFeature
+ */
+ public void onParametersUpdated(@NonNull Map<String, String> parameters) {}
}
}
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index b62196902570..864d17c2de9f 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -24,6 +24,7 @@ import android.util.Log;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Implements the RadioTuner interface by forwarding calls to radio service.
@@ -251,6 +252,24 @@ class TunerAdapter extends RadioTuner {
}
@Override
+ public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
+ try {
+ return mTuner.setParameters(Objects.requireNonNull(parameters));
+ } catch (RemoteException e) {
+ throw new RuntimeException("service died", e);
+ }
+ }
+
+ @Override
+ public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
+ try {
+ return mTuner.getParameters(Objects.requireNonNull(keys));
+ } catch (RemoteException e) {
+ throw new RuntimeException("service died", e);
+ }
+ }
+
+ @Override
public boolean isAntennaConnected() {
try {
return mTuner.isAntennaConnected();
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index ffd5b30fa15c..a01f658e80f6 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -22,6 +22,8 @@ import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import java.util.Map;
+
/**
* Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
*/
@@ -94,4 +96,9 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
public void onProgramListChanged() {
mHandler.post(() -> mCallback.onProgramListChanged());
}
+
+ @Override
+ public void onParametersUpdated(Map parameters) {
+ mHandler.post(() -> mCallback.onParametersUpdated(parameters));
+ }
}
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
index 8f1a5c42e6ac..af9a73ca31ee 100644
--- a/core/java/android/net/metrics/WakeupEvent.java
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -29,7 +29,7 @@ public class WakeupEvent {
public String iface;
public int uid;
public int ethertype;
- public byte[] dstHwAddr;
+ public MacAddress dstHwAddr;
public String srcIp;
public String dstIp;
public int ipNextHeader;
@@ -44,7 +44,7 @@ public class WakeupEvent {
j.add(iface);
j.add("uid: " + Integer.toString(uid));
j.add("eth=0x" + Integer.toHexString(ethertype));
- j.add("dstHw=" + MacAddress.stringAddrFromByteAddr(dstHwAddr));
+ j.add("dstHw=" + dstHwAddr);
if (ipNextHeader > 0) {
j.add("ipNxtHdr=" + ipNextHeader);
j.add("srcIp=" + srcIp);
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 1ba97771b595..23c1f20f1a44 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -16,7 +16,6 @@
package android.net.metrics;
-import android.net.MacAddress;
import android.os.Process;
import android.os.SystemClock;
import android.util.SparseIntArray;
@@ -80,7 +79,7 @@ public class WakeupStats {
break;
}
- switch (MacAddress.macAddressType(ev.dstHwAddr)) {
+ switch (ev.dstHwAddr.addressType()) {
case UNICAST:
l2UnicastCount++;
break;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 935f5f3b78a7..6ebf3b9a5e9c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -103,7 +103,7 @@ public class Build {
/**
* A hardware serial number, if available. Alphanumeric only, case-insensitive.
- * For apps targeting SDK higher than {@link Build.VERSION_CODES#N_MR1} this
+ * For apps targeting SDK higher than {@link Build.VERSION_CODES#O_MR1} this
* field is set to {@link Build#UNKNOWN}.
*
* @deprecated Use {@link #getSerial()} instead.
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 07c6055137a3..f2e0bddb93aa 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,6 +22,7 @@ import android.content.pm.PackageManager;
import android.opengl.EGL14;
import android.os.Build;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.Log;
import dalvik.system.VMRuntime;
@@ -29,18 +30,110 @@ import dalvik.system.VMRuntime;
import java.io.File;
/** @hide */
-public final class GraphicsEnvironment {
+public class GraphicsEnvironment {
+
+ private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
+
+ /**
+ * Returns the shared {@link GraphicsEnvironment} instance.
+ */
+ public static GraphicsEnvironment getInstance() {
+ return sInstance;
+ }
private static final boolean DEBUG = false;
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+ private ClassLoader mClassLoader;
+ private String mLayerPath;
+ private String mDebugLayerPath;
+
+ /**
+ * Set up GraphicsEnvironment
+ */
+ public void setup(Context context) {
+ setupGpuLayers(context);
+ chooseDriver(context);
+ }
+
+ /**
+ * Check whether application is debuggable
+ */
+ private static boolean isDebuggable(Context context) {
+ return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0;
+ }
+
+ /**
+ * Store the layer paths available to the loader.
+ */
+ public void setLayerPaths(ClassLoader classLoader,
+ String layerPath,
+ String debugLayerPath) {
+ // We have to store these in the class because they are set up before we
+ // have access to the Context to properly set up GraphicsEnvironment
+ mClassLoader = classLoader;
+ mLayerPath = layerPath;
+ mDebugLayerPath = debugLayerPath;
+ }
+
+ /**
+ * Set up layer search paths for all apps
+ * If debuggable, check for additional debug settings
+ */
+ private void setupGpuLayers(Context context) {
+
+ String layerPaths = "";
+
+ // Only enable additional debug functionality if the following conditions are met:
+ // 1. App is debuggable
+ // 2. ENABLE_GPU_DEBUG_LAYERS is true
+ // 3. Package name is equal to GPU_DEBUG_APP
+
+ if (isDebuggable(context)) {
+
+ int enable = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+
+ if (enable != 0) {
+
+ String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.GPU_DEBUG_APP);
+
+ String packageName = context.getPackageName();
+
+ if ((gpuDebugApp != null && packageName != null)
+ && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
+ && gpuDebugApp.equals(packageName)) {
+ Log.i(TAG, "GPU debug layers enabled for " + packageName);
+
+ // Prepend the debug layer path as a searchable path.
+ // This will ensure debug layers added will take precedence over
+ // the layers specified by the app.
+ layerPaths = mDebugLayerPath + ":";
+
+ String layers = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.GPU_DEBUG_LAYERS);
+
+ Log.i(TAG, "Debug layer list: " + layers);
+ if (layers != null && !layers.isEmpty()) {
+ setDebugLayers(layers);
+ }
+ }
+ }
+
+ }
+
+ // Include the app's lib directory in all cases
+ layerPaths += mLayerPath;
+
+ setLayerPaths(mClassLoader, layerPaths);
+ }
+
/**
* Choose whether the current process should use the builtin or an updated driver.
- *
- * @hide
*/
- public static void chooseDriver(Context context) {
+ private static void chooseDriver(Context context) {
String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
if (driverPackageName == null || driverPackageName.isEmpty()) {
return;
@@ -99,8 +192,6 @@ public final class GraphicsEnvironment {
* on a separate thread, it can usually be finished well before the UI is ready to be drawn.
*
* Should only be called after chooseDriver().
- *
- * @hide
*/
public static void earlyInitEGL() {
Thread eglInitThread = new Thread(
@@ -124,6 +215,7 @@ public final class GraphicsEnvironment {
return null;
}
+ private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
+ private static native void setDebugLayers(String layers);
private static native void setDriverPath(String path);
-
}
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index a6c3009acba2..8e33b652a87e 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -16,8 +16,6 @@
package android.os;
-import android.os.IStatsCallbacks;
-
/**
* Binder interface to communicate with the statistics management service.
* {@hide}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 5d96fd35a62e..0620fa3dd084 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,6 +35,10 @@ interface IUserManager {
* DO NOT MOVE - UserManager.h depends on the ordering of this function.
*/
int getCredentialOwnerProfile(int userHandle);
+ int getProfileParentId(int userHandle);
+ /*
+ * END OF DO NOT MOVE
+ */
UserInfo createUser(in String name, int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle,
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 10adb5a61aaf..d0920187498f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -191,6 +191,7 @@ import java.util.Set;
* {@link #readSparseArray(ClassLoader)}.
*/
public final class Parcel {
+
private static final boolean DEBUG_RECYCLE = false;
private static final boolean DEBUG_ARRAY_MAP = false;
private static final String TAG = "Parcel";
@@ -209,6 +210,12 @@ public final class Parcel {
private RuntimeException mStack;
+ /**
+ * Whether or not to parcel the stack trace of an exception. This has a performance
+ * impact, so should only be included in specific processes and only on debug builds.
+ */
+ private static boolean sParcelExceptionStackTrace;
+
private static final int POOL_SIZE = 6;
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
@@ -325,6 +332,11 @@ public final class Parcel {
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
private static native void nativeEnforceInterface(long nativePtr, String interfaceName);
+ /** Last time exception with a stack trace was written */
+ private static volatile long sLastWriteExceptionStackTrace;
+ /** Used for throttling of writing stack trace, which is costly */
+ private static final int WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS = 1000;
+
@CriticalNative
private static native long nativeGetBlobAshmemSize(long nativePtr);
@@ -1696,6 +1708,11 @@ public final class Parcel {
}
}
+ /** @hide For debugging purposes */
+ public static void setStackTraceParceling(boolean enabled) {
+ sParcelExceptionStackTrace = enabled;
+ }
+
/**
* Special function for writing an exception result at the header of
* a parcel, to be used when returning an exception from a transaction.
@@ -1753,6 +1770,27 @@ public final class Parcel {
throw new RuntimeException(e);
}
writeString(e.getMessage());
+ final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0;
+ if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace
+ > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) {
+ sLastWriteExceptionStackTrace = timeNow;
+ final int sizePosition = dataPosition();
+ writeInt(0); // Header size will be filled in later
+ StackTraceElement[] stackTrace = e.getStackTrace();
+ final int truncatedSize = Math.min(stackTrace.length, 5);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < truncatedSize; i++) {
+ sb.append("\tat ").append(stackTrace[i]);
+ }
+ writeString(sb.toString());
+ final int payloadPosition = dataPosition();
+ setDataPosition(sizePosition);
+ // Write stack trace header size. Used in native side to skip the header
+ writeInt(payloadPosition - sizePosition);
+ setDataPosition(payloadPosition);
+ } else {
+ writeInt(0);
+ }
switch (code) {
case EX_SERVICE_SPECIFIC:
writeInt(((ServiceSpecificException) e).errorCode);
@@ -1818,7 +1856,19 @@ public final class Parcel {
int code = readExceptionCode();
if (code != 0) {
String msg = readString();
- readException(code, msg);
+ String remoteStackTrace = null;
+ final int remoteStackPayloadSize = readInt();
+ if (remoteStackPayloadSize > 0) {
+ remoteStackTrace = readString();
+ }
+ Exception e = createException(code, msg);
+ // Attach remote stack trace if availalble
+ if (remoteStackTrace != null) {
+ RemoteException cause = new RemoteException(
+ "Remote stack trace:\n" + remoteStackTrace, null, false, false);
+ e.initCause(cause);
+ }
+ SneakyThrow.sneakyThrow(e);
}
}
@@ -1863,32 +1913,41 @@ public final class Parcel {
* @param msg The exception message.
*/
public final void readException(int code, String msg) {
+ SneakyThrow.sneakyThrow(createException(code, msg));
+ }
+
+ /**
+ * Creates an exception with the given message.
+ *
+ * @param code Used to determine which exception class to throw.
+ * @param msg The exception message.
+ */
+ private Exception createException(int code, String msg) {
switch (code) {
case EX_PARCELABLE:
if (readInt() > 0) {
- SneakyThrow.sneakyThrow(
- (Exception) readParcelable(Parcelable.class.getClassLoader()));
+ return (Exception) readParcelable(Parcelable.class.getClassLoader());
} else {
- throw new RuntimeException(msg + " [missing Parcelable]");
+ return new RuntimeException(msg + " [missing Parcelable]");
}
case EX_SECURITY:
- throw new SecurityException(msg);
+ return new SecurityException(msg);
case EX_BAD_PARCELABLE:
- throw new BadParcelableException(msg);
+ return new BadParcelableException(msg);
case EX_ILLEGAL_ARGUMENT:
- throw new IllegalArgumentException(msg);
+ return new IllegalArgumentException(msg);
case EX_NULL_POINTER:
- throw new NullPointerException(msg);
+ return new NullPointerException(msg);
case EX_ILLEGAL_STATE:
- throw new IllegalStateException(msg);
+ return new IllegalStateException(msg);
case EX_NETWORK_MAIN_THREAD:
- throw new NetworkOnMainThreadException();
+ return new NetworkOnMainThreadException();
case EX_UNSUPPORTED_OPERATION:
- throw new UnsupportedOperationException(msg);
+ return new UnsupportedOperationException(msg);
case EX_SERVICE_SPECIFIC:
- throw new ServiceSpecificException(readInt(), msg);
+ return new ServiceSpecificException(readInt(), msg);
}
- throw new RuntimeException("Unknown exception code: " + code
+ return new RuntimeException("Unknown exception code: " + code
+ " msg " + msg);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dd4825ef5058..0fce7a454717 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -527,8 +527,7 @@ public final class PowerManager {
ServiceType.SOUND,
ServiceType.BATTERY_STATS,
ServiceType.DATA_SAVER,
- ServiceType.FORCE_ALL_APPS_STANDBY_JOBS,
- ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS,
+ ServiceType.FORCE_ALL_APPS_STANDBY,
ServiceType.OPTIONAL_SENSORS,
})
public @interface ServiceType {
@@ -545,14 +544,9 @@ public final class PowerManager {
int DATA_SAVER = 10;
/**
- * Whether the job scheduler should force app standby on all apps on battery saver or not.
+ * Whether to enable force-app-standby on all apps or not.
*/
- int FORCE_ALL_APPS_STANDBY_JOBS = 11;
-
- /**
- * Whether the alarm manager should force app standby on all apps on battery saver or not.
- */
- int FORCE_ALL_APPS_STANDBY_ALARMS = 12;
+ int FORCE_ALL_APPS_STANDBY = 11;
/**
* Whether to disable non-essential sensors. (e.g. edge sensors.)
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b5d62e555edc..0874d93e8262 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -151,6 +151,9 @@ public class Process {
*/
public static final int OTA_UPDATE_UID = 1061;
+ /** {@hide} */
+ public static final int NOBODY_UID = 9999;
+
/**
* Defines the start of a range of UIDs (and GIDs), going from this
* number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 6d25fc17bd6a..4e8b9716e852 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -30,6 +30,12 @@ public class RemoteException extends AndroidException {
super(message);
}
+ /** @hide */
+ public RemoteException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
/** {@hide} */
public RuntimeException rethrowAsRuntimeException() {
throw new RuntimeException(this);
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
index 5e0a76de8d27..a60be5363d62 100644
--- a/core/java/android/provider/SearchIndexableData.java
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -56,6 +56,8 @@ public abstract class SearchIndexableData {
/**
* The key for the data. This is application specific. Should be unique per data as the data
* should be able to be retrieved by the key.
+ * <p/>
+ * This is required for indexing to work.
*/
public String key;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6decc3050f64..994575545c5c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9854,6 +9854,27 @@ public final class Settings {
public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
/**
+ * Allow GPU debug layers?
+ * 0 = no
+ * 1 = yes
+ * @hide
+ */
+ public static final String ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers";
+
+ /**
+ * App allowed to load GPU debug layers
+ * @hide
+ */
+ public static final String GPU_DEBUG_APP = "gpu_debug_app";
+
+ /**
+ * Ordered GPU debug layer list
+ * i.e. <layer1>:<layer2>:...:<layerN>
+ * @hide
+ */
+ public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
+
+ /**
* Control whether the process CPU usage meter should be shown.
*
* @deprecated This functionality is no longer available as of
diff --git a/core/java/android/service/autofill/InternalSanitizer.java b/core/java/android/service/autofill/InternalSanitizer.java
index 95d2f660b24b..d77e41e3f022 100644
--- a/core/java/android/service/autofill/InternalSanitizer.java
+++ b/core/java/android/service/autofill/InternalSanitizer.java
@@ -16,6 +16,7 @@
package android.service.autofill;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcelable;
import android.view.autofill.AutofillValue;
@@ -32,7 +33,11 @@ public abstract class InternalSanitizer implements Sanitizer, Parcelable {
/**
* Sanitizes an {@link AutofillValue}.
*
+ * @return sanitized value or {@code null} if value could not be sanitized (for example: didn't
+ * match regex, it's an invalid type, regex failed, etc).
+ *
* @hide
*/
+ @Nullable
public abstract AutofillValue sanitize(@NonNull AutofillValue value);
}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 9a1dcbb2785f..bc4b3fc3d5ea 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -613,6 +613,11 @@ public final class SaveInfo implements Parcelable {
* usernameId, passwordId);
* </pre>
*
+ * <p>The sanitizer can also be used as an alternative for a
+ * {@link #setValidator(Validator) validator}&mdashif any of the {@code ids} is a
+ * {@link #SaveInfo.Builder(int, AutofillId[]) required id} and the {@code sanitizer} fail
+ * for it, then the save UI is not shown.
+ *
* @param sanitizer an implementation provided by the Android System.
* @param ids id of fields whose value will be sanitized.
* @return this builder.
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
index 12e85b1db490..a3a98d8e6390 100644
--- a/core/java/android/service/autofill/TextValueSanitizer.java
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -19,6 +19,7 @@ package android.service.autofill;
import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -62,24 +63,31 @@ public final class TextValueSanitizer extends InternalSanitizer implements
/** @hide */
@Override
@TestApi
+ @Nullable
public AutofillValue sanitize(@NonNull AutofillValue value) {
if (value == null) {
Slog.w(TAG, "sanitize() called with null value");
return null;
}
- if (!value.isText()) return value;
+ if (!value.isText()) {
+ if (sDebug) Slog.d(TAG, "sanitize() called with non-text value: " + value);
+ return null;
+ }
final CharSequence text = value.getTextValue();
try {
final Matcher matcher = mRegex.matcher(text);
- if (!matcher.matches()) return value;
+ if (!matcher.matches()) {
+ if (sDebug) Slog.d(TAG, "sanitize(): " + mRegex + " failed for " + value);
+ return null;
+ }
final CharSequence sanitized = matcher.replaceAll(mSubst);
return AutofillValue.forText(sanitized);
} catch (Exception e) {
Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
- return value;
+ return null;
}
}
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 2c83fc4d9049..8c90156d159d 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -355,21 +355,21 @@ public final class Formatter {
final Locale locale = localeFromContext(context);
final MeasureFormat measureFormat = MeasureFormat.getInstance(
locale, MeasureFormat.FormatWidth.SHORT);
- if (days >= 2) {
+ if (days >= 2 || (days > 0 && hours == 0)) {
days += (hours+12)/24;
return measureFormat.format(new Measure(days, MeasureUnit.DAY));
} else if (days > 0) {
return measureFormat.formatMeasures(
new Measure(days, MeasureUnit.DAY),
new Measure(hours, MeasureUnit.HOUR));
- } else if (hours >= 2) {
+ } else if (hours >= 2 || (hours > 0 && minutes == 0)) {
hours += (minutes+30)/60;
return measureFormat.format(new Measure(hours, MeasureUnit.HOUR));
} else if (hours > 0) {
return measureFormat.formatMeasures(
new Measure(hours, MeasureUnit.HOUR),
new Measure(minutes, MeasureUnit.MINUTE));
- } else if (minutes >= 2) {
+ } else if (minutes >= 2 || (minutes > 0 && seconds == 0)) {
minutes += (seconds+30)/60;
return measureFormat.format(new Measure(minutes, MeasureUnit.MINUTE));
} else if (minutes > 0) {
diff --git a/core/java/android/util/AndroidException.java b/core/java/android/util/AndroidException.java
index dfe00c9bd49f..1345ddf189e1 100644
--- a/core/java/android/util/AndroidException.java
+++ b/core/java/android/util/AndroidException.java
@@ -34,5 +34,11 @@ public class AndroidException extends Exception {
public AndroidException(Exception cause) {
super(cause);
}
+
+ /** @hide */
+ protected AndroidException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
};
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2a2722082a7d..29baea17da33 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -18,6 +18,7 @@ package android.util;
import android.content.Context;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.text.TextUtils;
import java.util.Map;
@@ -39,13 +40,24 @@ public class FeatureFlagUtils {
* @return true if the flag is enabled (either by default in system, or override by user)
*/
public static boolean isEnabled(Context context, String feature) {
- // Tries to get feature flag from system property.
- // Step 1: check if feature flag has any override. Flag name: sys.fflag.override.<feature>
- String value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature);
+ // Override precedence:
+ // Settings.Global -> sys.fflag.override.* -> sys.fflag.*
+
+ // Step 1: check if feature flag is set in Settings.Global.
+ String value;
+ if (context != null) {
+ value = Settings.Global.getString(context.getContentResolver(), feature);
+ if (!TextUtils.isEmpty(value)) {
+ return Boolean.parseBoolean(value);
+ }
+ }
+
+ // Step 2: check if feature flag has any override. Flag name: sys.fflag.override.<feature>
+ value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature);
if (!TextUtils.isEmpty(value)) {
return Boolean.parseBoolean(value);
}
- // Step 2: check if feature flag has any default value. Flag name: sys.fflag.<feature>
+ // Step 3: check if feature flag has any default value. Flag name: sys.fflag.<feature>
value = SystemProperties.get(FFLAG_PREFIX + feature);
return Boolean.parseBoolean(value);
}
@@ -53,7 +65,7 @@ public class FeatureFlagUtils {
/**
* Override feature flag to new state.
*/
- public static void setEnabled(String feature, boolean enabled) {
+ public static void setEnabled(Context context, String feature, boolean enabled) {
SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false");
}
diff --git a/core/java/android/os/IStatsCallbacks.aidl b/core/java/android/util/MutableInt.java
index 02e7cd3978c8..a3d8606d916a 100644
--- a/core/java/android/os/IStatsCallbacks.aidl
+++ b/core/java/android/util/MutableInt.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package android.os;
+package android.util;
/**
- * Callback for Statsd to allow binder calls to clients.
- * {@hide}
- */
-interface IStatsCallbacks {
- void onReceiveLogs(out byte[] log);
+ */
+public final class MutableInt {
+ public int value;
+
+ public MutableInt(int value) {
+ this.value = value;
+ }
}
diff --git a/core/java/android/util/MutableLong.java b/core/java/android/util/MutableLong.java
new file mode 100644
index 000000000000..575068ea9364
--- /dev/null
+++ b/core/java/android/util/MutableLong.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ */
+public final class MutableLong {
+ public long value;
+
+ public MutableLong(long value) {
+ this.value = value;
+ }
+}
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
index 55b33a617a60..2bcd863cd409 100644
--- a/core/java/android/util/StatsManager.java
+++ b/core/java/android/util/StatsManager.java
@@ -58,11 +58,12 @@ public final class StatsManager {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- throw new RuntimeException("StatsD service connection lost");
+ Slog.d(TAG, "Failed to find statsd when adding configuration");
+ return false;
}
return service.addConfiguration(configKey, config, pkg, cls);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when getting data");
+ Slog.d(TAG, "Failed to connect to statsd when adding configuration");
return false;
}
}
@@ -80,11 +81,12 @@ public final class StatsManager {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- throw new RuntimeException("StatsD service connection lost");
+ Slog.d(TAG, "Failed to find statsd when removing configuration");
+ return false;
}
return service.removeConfiguration(configKey);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when getting data");
+ Slog.d(TAG, "Failed to connect to statsd when removing configuration");
return false;
}
}
@@ -102,7 +104,8 @@ public final class StatsManager {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- throw new RuntimeException("StatsD service connection lost");
+ Slog.d(TAG, "Failed to find statsd when getting data");
+ return null;
}
return service.getData(configKey);
} catch (RemoteException e) {
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 449baca5c844..85b7ec8265d1 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -17,6 +17,7 @@
package android.util.proto;
import android.util.AggStats;
+import android.util.Duration;
/**
* This class contains a list of helper functions to write common proto in
@@ -36,4 +37,15 @@ public class ProtoUtils {
proto.write(AggStats.MAX, max);
proto.end(aggStatsToken);
}
+
+ /**
+ * Dump Duration to ProtoOutputStream
+ * @hide
+ */
+ public static void toDuration(ProtoOutputStream proto, long fieldId, long startMs, long endMs) {
+ final long token = proto.start(fieldId);
+ proto.write(Duration.START_MS, startMs);
+ proto.write(Duration.END_MS, endMs);
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index 232ff2551433..defa58e10e6e 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -446,50 +446,53 @@ public class Gravity
*/
public static String toString(int gravity) {
final StringBuilder result = new StringBuilder();
- if ((gravity & FILL) != 0) {
+ if ((gravity & FILL) == FILL) {
result.append("FILL").append(' ');
} else {
- if ((gravity & FILL_VERTICAL) != 0) {
+ if ((gravity & FILL_VERTICAL) == FILL_VERTICAL) {
result.append("FILL_VERTICAL").append(' ');
} else {
- if ((gravity & TOP) != 0) {
+ if ((gravity & TOP) == TOP) {
result.append("TOP").append(' ');
}
- if ((gravity & BOTTOM) != 0) {
+ if ((gravity & BOTTOM) == BOTTOM) {
result.append("BOTTOM").append(' ');
}
}
- if ((gravity & FILL_HORIZONTAL) != 0) {
+ if ((gravity & FILL_HORIZONTAL) == FILL_HORIZONTAL) {
result.append("FILL_HORIZONTAL").append(' ');
} else {
- if ((gravity & START) != 0) {
+ if ((gravity & START) == START) {
result.append("START").append(' ');
- } else if ((gravity & LEFT) != 0) {
+ } else if ((gravity & LEFT) == LEFT) {
result.append("LEFT").append(' ');
}
- if ((gravity & END) != 0) {
+ if ((gravity & END) == END) {
result.append("END").append(' ');
- } else if ((gravity & RIGHT) != 0) {
+ } else if ((gravity & RIGHT) == RIGHT) {
result.append("RIGHT").append(' ');
}
}
}
- if ((gravity & CENTER) != 0) {
+ if ((gravity & CENTER) == CENTER) {
result.append("CENTER").append(' ');
} else {
- if ((gravity & CENTER_VERTICAL) != 0) {
+ if ((gravity & CENTER_VERTICAL) == CENTER_VERTICAL) {
result.append("CENTER_VERTICAL").append(' ');
}
- if ((gravity & CENTER_HORIZONTAL) != 0) {
+ if ((gravity & CENTER_HORIZONTAL) == CENTER_HORIZONTAL) {
result.append("CENTER_HORIZONTAL").append(' ');
}
}
- if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
- result.append("DISPLAY_CLIP_VERTICAL").append(' ');
+ if (result.length() == 0) {
+ result.append("NO GRAVITY").append(' ');
}
- if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
+ if ((gravity & DISPLAY_CLIP_VERTICAL) == DISPLAY_CLIP_VERTICAL) {
result.append("DISPLAY_CLIP_VERTICAL").append(' ');
}
+ if ((gravity & DISPLAY_CLIP_HORIZONTAL) == DISPLAY_CLIP_HORIZONTAL) {
+ result.append("DISPLAY_CLIP_HORIZONTAL").append(' ');
+ }
result.deleteCharAt(result.length() - 1);
return result.toString();
}
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 95150409514d..c4a716011765 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -19,7 +19,6 @@ package android.view;
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.graphics.Canvas;
import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.util.SparseIntArray;
@@ -281,12 +280,9 @@ public class RenderNodeAnimator extends Animator {
setTarget(mViewTarget.mRenderNode);
}
- public void setTarget(Canvas canvas) {
- if (!(canvas instanceof DisplayListCanvas)) {
- throw new IllegalArgumentException("Not a GLES20RecordingCanvas");
- }
- final DisplayListCanvas recordingCanvas = (DisplayListCanvas) canvas;
- setTarget(recordingCanvas.mNode);
+ /** Sets the animation target to the owning view of the DisplayListCanvas */
+ public void setTarget(DisplayListCanvas canvas) {
+ setTarget(canvas.mNode);
}
private void setTarget(RenderNode node) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5641009c1320..aa2f1c1a9e24 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -61,6 +61,8 @@ public class SurfaceControl {
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
private static native void nativeApplyTransaction(long transactionObj, boolean sync);
+ private static native void nativeMergeTransaction(long transactionObj,
+ long otherTransactionObj);
private static native void nativeSetAnimationTransaction(long transactionObj);
private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
@@ -654,6 +656,19 @@ public class SurfaceControl {
}
}
+ /**
+ * Merge the supplied transaction in to the deprecated "global" transaction.
+ * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
+ * <p>
+ * This is a utility for interop with legacy-code and will go away with the Global Transaction.
+ */
+ @Deprecated
+ public static void mergeToGlobalTransaction(Transaction t) {
+ synchronized(sGlobalTransaction) {
+ sGlobalTransaction.merge(t);
+ }
+ }
+
/** end a transaction */
public static void closeTransaction() {
closeTransaction(false);
@@ -1368,7 +1383,7 @@ public class SurfaceControl {
* Sets the security of the surface. Setting the flag is equivalent to creating the
* Surface with the {@link #SECURE} flag.
*/
- Transaction setSecure(SurfaceControl sc, boolean isSecure) {
+ public Transaction setSecure(SurfaceControl sc, boolean isSecure) {
sc.checkNotReleased();
if (isSecure) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
@@ -1449,5 +1464,14 @@ public class SurfaceControl {
nativeSetAnimationTransaction(mNativeObject);
return this;
}
+
+ /**
+ * Merge the other transaction into this transaction, clearing the
+ * other transaction as if it had been applied.
+ */
+ public Transaction merge(Transaction other) {
+ nativeMergeTransaction(mNativeObject, other.mNativeObject);
+ return this;
+ }
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index be09fe869518..19ce43febf69 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4163,6 +4163,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static boolean sUseDefaultFocusHighlight;
+ /**
+ * True if zero-sized views can be focused.
+ */
+ private static boolean sCanFocusZeroSized;
+
private String mTransitionName;
static class TintInfo {
@@ -4245,6 +4250,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
OnCapturedPointerListener mOnCapturedPointerListener;
+
+ private ArrayList<OnKeyFallbackListener> mKeyFallbackListeners;
}
ListenerInfo mListenerInfo;
@@ -4743,6 +4750,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sUseDefaultFocusHighlight = context.getResources().getBoolean(
com.android.internal.R.bool.config_useDefaultFocusHighlight);
+ sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -6950,6 +6959,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
+ clearParentsWantFocus();
if (propagate && mParent != null) {
mParent.clearChildFocus(this);
@@ -10931,8 +10941,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* descendants.
*
* A view will not actually take focus if it is not focusable ({@link #isFocusable} returns
- * false), or if it is focusable and it is not focusable in touch mode
- * ({@link #isFocusableInTouchMode}) while the device is in touch mode.
+ * false), or if it can't be focused due to other conditions (not focusable in touch mode
+ * ({@link #isFocusableInTouchMode}) while the device is in touch mode, not visible, not
+ * enabled, or has no size).
*
* See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
@@ -10970,17 +10981,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@TestApi
public boolean restoreFocusNotInCluster() {
- return requestFocus(View.FOCUS_DOWN);
+ return requestFocus();
}
/**
* Gives focus to the default-focus view in the view hierarchy that has this view as a root.
- * If the default-focus view cannot be found, falls back to calling {@link #requestFocus(int)}.
+ * If the default-focus view cannot be found, falls back to calling {@link #requestFocus()}.
*
* @return Whether this view or one of its descendants actually took focus
*/
public boolean restoreDefaultFocus() {
- return requestFocus(View.FOCUS_DOWN);
+ return requestFocus();
}
/**
@@ -11039,9 +11050,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
// need to be focusable
- if ((mViewFlags & FOCUSABLE) != FOCUSABLE
- || (mViewFlags & VISIBILITY_MASK) != VISIBLE
- || (mViewFlags & ENABLED_MASK) != ENABLED) {
+ if (!canTakeFocus()) {
return false;
}
@@ -11056,10 +11065,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return false;
}
+ if (!isLaidOut()) {
+ mPrivateFlags |= PFLAG_WANTS_FOCUS;
+ }
+
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
+ void clearParentsWantFocus() {
+ if (mParent instanceof View) {
+ ((View) mParent).mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+ ((View) mParent).clearParentsWantFocus();
+ }
+ }
+
/**
* Call this to try to give focus to a specific view or to one of its descendants. This is a
* special variant of {@link #requestFocus() } that will allow views that are not focusable in
@@ -13431,6 +13451,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo.mUnbufferedDispatchRequested = true;
}
+ private boolean canTakeFocus() {
+ return ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
+ && ((mViewFlags & FOCUSABLE) == FOCUSABLE)
+ && ((mViewFlags & ENABLED_MASK) == ENABLED)
+ && (sCanFocusZeroSized || !isLaidOut() || (mBottom > mTop) && (mRight > mLeft));
+ }
+
/**
* Set flags controlling behavior of this view.
*
@@ -13450,6 +13477,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return;
}
int privateFlags = mPrivateFlags;
+ boolean shouldNotifyFocusableAvailable = false;
// If focusable is auto, update the FOCUSABLE bit.
int focusableChangedByAuto = 0;
@@ -13488,7 +13516,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|| focusableChangedByAuto == 0
|| viewRootImpl == null
|| viewRootImpl.mThread == Thread.currentThread()) {
- mParent.focusableViewAvailable(this);
+ shouldNotifyFocusableAvailable = true;
}
}
}
@@ -13511,10 +13539,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// about in case nothing has focus. even if this specific view
// isn't focusable, it may contain something that is, so let
// the root view try to give this focus if nothing else does.
- if ((mParent != null) && ((mViewFlags & ENABLED_MASK) == ENABLED)
- && (mBottom > mTop) && (mRight > mLeft)) {
- mParent.focusableViewAvailable(this);
- }
+ shouldNotifyFocusableAvailable = true;
}
}
@@ -13523,17 +13548,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// a view becoming enabled should notify the parent as long as the view is also
// visible and the parent wasn't already notified by becoming visible during this
// setFlags invocation.
- if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
- && ((changed & VISIBILITY_MASK) == 0)) {
- if ((mParent != null) && (mViewFlags & ENABLED_MASK) == ENABLED) {
- mParent.focusableViewAvailable(this);
- }
- }
+ shouldNotifyFocusableAvailable = true;
} else {
if (hasFocus()) clearFocus();
}
}
+ if (shouldNotifyFocusableAvailable) {
+ if (mParent != null && canTakeFocus()) {
+ mParent.focusableViewAvailable(this);
+ }
+ }
+
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0) {
needGlobalAttributesUpdate(false);
@@ -20032,15 +20058,58 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ final boolean wasLaidOut = isLaidOut();
+
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
+ if (!wasLaidOut && isFocused()) {
+ mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+ if (canTakeFocus()) {
+ // We have a robust focus, so parents should no longer be wanting focus.
+ clearParentsWantFocus();
+ } else if (!getViewRootImpl().isInLayout()) {
+ // This is a weird case. Most-likely the user, rather than ViewRootImpl, called
+ // layout. In this case, there's no guarantee that parent layouts will be evaluated
+ // and thus the safest action is to clear focus here.
+ clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+ clearParentsWantFocus();
+ } else if (!hasParentWantsFocus()) {
+ // original requestFocus was likely on this view directly, so just clear focus
+ clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+ }
+ // otherwise, we let parents handle re-assigning focus during their layout passes.
+ } else if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
+ mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+ View focused = findFocus();
+ if (focused != null) {
+ // Try to restore focus as close as possible to our starting focus.
+ if (!restoreDefaultFocus() && !hasParentWantsFocus()) {
+ // Give up and clear focus once we've reached the top-most parent which wants
+ // focus.
+ focused.clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+ }
+ }
+ }
+
if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
notifyEnterOrExitForAutoFillIfNeeded(true);
}
}
+ private boolean hasParentWantsFocus() {
+ ViewParent parent = mParent;
+ while (parent instanceof ViewGroup) {
+ ViewGroup pv = (ViewGroup) parent;
+ if ((pv.mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
+ return true;
+ }
+ parent = pv.mParent;
+ }
+ return false;
+ }
+
/**
* Called from layout when this view should
* assign a size and position to each of its children.
@@ -20147,6 +20216,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
+ // If this isn't laid out yet, focus assignment will be handled during the "deferment/
+ // backtracking" of requestFocus during layout, so don't touch focus here.
+ if (!sCanFocusZeroSized && isLaidOut()) {
+ if (newWidth <= 0 || newHeight <= 0) {
+ if (hasFocus()) {
+ clearFocus();
+ if (mParent instanceof ViewGroup) {
+ ((ViewGroup) mParent).clearFocusedInCluster();
+ }
+ }
+ clearAccessibilityFocus();
+ } else if (oldWidth <= 0 || oldHeight <= 0) {
+ if (mParent != null && canTakeFocus()) {
+ mParent.focusableViewAvailable(this);
+ }
+ }
+ }
rebuildOutline();
}
@@ -25194,6 +25280,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Interface definition for a callback to be invoked when a hardware key event is
+ * dispatched to this view during the fallback phase. This means no view in the hierarchy
+ * has handled this event.
+ */
+ public interface OnKeyFallbackListener {
+ /**
+ * Called when a hardware key is dispatched to a view in the fallback phase. This allows
+ * listeners to respond to events after the view hierarchy has had a chance to respond.
+ * <p>Key presses in software keyboards will generally NOT trigger this method,
+ * although some may elect to do so in some situations. Do not assume a
+ * software input method has to be key-based; even if it is, it may use key presses
+ * in a different way than you expect, so there is no way to reliably catch soft
+ * input key presses.
+ *
+ * @param v The view the key has been dispatched to.
+ * @param event The KeyEvent object containing full information about
+ * the event.
+ * @return True if the listener has consumed the event, false otherwise.
+ */
+ boolean onKeyFallback(View v, KeyEvent event);
+ }
+
+ /**
* Interface definition for a callback to be invoked when a touch event is
* dispatched to this view. The callback will be invoked before the touch
* event is given to the view.
@@ -26866,4 +26975,56 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
return mTooltipInfo.mTooltipPopup.getContentView();
}
+
+ /**
+ * Allows this view to handle {@link KeyEvent}s which weren't handled by normal dispatch. This
+ * occurs after the normal view hierarchy dispatch, but before the window callback. By default,
+ * this will dispatch into all the listeners registered via
+ * {@link #addKeyFallbackListener(OnKeyFallbackListener)} in last-in-first-out order (most
+ * recently added will receive events first).
+ *
+ * @param event A not-previously-handled event.
+ * @return {@code true} if the event was handled, {@code false} otherwise.
+ * @see #addKeyFallbackListener
+ */
+ public boolean onKeyFallback(@NonNull KeyEvent event) {
+ if (mListenerInfo != null && mListenerInfo.mKeyFallbackListeners != null) {
+ for (int i = mListenerInfo.mKeyFallbackListeners.size() - 1; i >= 0; --i) {
+ if (mListenerInfo.mKeyFallbackListeners.get(i).onKeyFallback(this, event)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds a listener which will receive unhandled {@link KeyEvent}s.
+ * @param listener the receiver of fallback {@link KeyEvent}s.
+ * @see #onKeyFallback(KeyEvent)
+ */
+ public void addKeyFallbackListener(OnKeyFallbackListener listener) {
+ ArrayList<OnKeyFallbackListener> fallbacks = getListenerInfo().mKeyFallbackListeners;
+ if (fallbacks == null) {
+ fallbacks = new ArrayList<>();
+ getListenerInfo().mKeyFallbackListeners = fallbacks;
+ }
+ fallbacks.add(listener);
+ }
+
+ /**
+ * Removes a listener which will receive unhandled {@link KeyEvent}s.
+ * @param listener the receiver of fallback {@link KeyEvent}s.
+ * @see #onKeyFallback(KeyEvent)
+ */
+ public void removeKeyFallbackListener(OnKeyFallbackListener listener) {
+ if (mListenerInfo != null) {
+ if (mListenerInfo.mKeyFallbackListeners != null) {
+ mListenerInfo.mKeyFallbackListeners.remove(listener);
+ if (mListenerInfo.mKeyFallbackListeners.isEmpty()) {
+ mListenerInfo.mKeyFallbackListeners = null;
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 929beaea42b8..d0fcc5dfb814 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3215,22 +3215,31 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
int descendantFocusability = getDescendantFocusability();
+ boolean result;
switch (descendantFocusability) {
case FOCUS_BLOCK_DESCENDANTS:
- return super.requestFocus(direction, previouslyFocusedRect);
+ result = super.requestFocus(direction, previouslyFocusedRect);
+ break;
case FOCUS_BEFORE_DESCENDANTS: {
final boolean took = super.requestFocus(direction, previouslyFocusedRect);
- return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ result = took ? took : onRequestFocusInDescendants(direction,
+ previouslyFocusedRect);
+ break;
}
case FOCUS_AFTER_DESCENDANTS: {
final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
- return took ? took : super.requestFocus(direction, previouslyFocusedRect);
+ result = took ? took : super.requestFocus(direction, previouslyFocusedRect);
+ break;
}
default:
throw new IllegalStateException("descendant focusability must be "
+ "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
+ "but is " + descendantFocusability);
}
+ if (result && !isLaidOut() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
+ mPrivateFlags |= PFLAG_WANTS_FOCUS;
+ }
+ return result;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e30496fb718b..e9509b798c76 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -73,6 +73,7 @@ import android.util.Log;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException;
@@ -362,6 +363,8 @@ public final class ViewRootImpl implements ViewParent,
InputStage mFirstPostImeInputStage;
InputStage mSyntheticInputStage;
+ private final KeyFallbackManager mKeyFallbackManager = new KeyFallbackManager();
+
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -4764,6 +4767,13 @@ public final class ViewRootImpl implements ViewParent,
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
+ mKeyFallbackManager.mDispatched = false;
+
+ if (mKeyFallbackManager.hasFocus()
+ && mKeyFallbackManager.dispatchUnique(mView, event)) {
+ return FINISH_HANDLED;
+ }
+
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
@@ -4773,6 +4783,10 @@ public final class ViewRootImpl implements ViewParent,
return FINISH_NOT_HANDLED;
}
+ if (mKeyFallbackManager.dispatchUnique(mView, event)) {
+ return FINISH_HANDLED;
+ }
+
int groupNavigationDirection = 0;
if (event.getAction() == KeyEvent.ACTION_DOWN
@@ -7529,6 +7543,16 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ /**
+ * Dispatches a KeyEvent to all registered key fallback handlers.
+ *
+ * @param event
+ * @return {@code true} if the event was handled, {@code false} otherwise.
+ */
+ public boolean dispatchKeyFallbackEvent(KeyEvent event) {
+ return mKeyFallbackManager.dispatch(mView, event);
+ }
+
class TakenSurfaceHolder extends BaseSurfaceHolder {
@Override
public boolean onAllowLockCanvas() {
@@ -8093,4 +8117,92 @@ public final class ViewRootImpl implements ViewParent,
run();
}
}
+
+ private static class KeyFallbackManager {
+
+ // This is used to ensure that key-fallback events are only dispatched once. We attempt
+ // to dispatch more than once in order to achieve a certain order. Specifically, if we
+ // are in an Activity or Dialog (and have a Window.Callback), the keyfallback events should
+ // be dispatched after the view hierarchy, but before the Activity. However, if we aren't
+ // in an activity, we still want key fallbacks to be dispatched.
+ boolean mDispatched = false;
+
+ SparseBooleanArray mCapturedKeys = new SparseBooleanArray();
+ WeakReference<View> mFallbackReceiver = null;
+ int mVisitCount = 0;
+
+ private void updateCaptureState(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ mCapturedKeys.append(event.getKeyCode(), true);
+ }
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ mCapturedKeys.delete(event.getKeyCode());
+ }
+ }
+
+ boolean dispatch(View root, KeyEvent event) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "KeyFallback dispatch");
+ mDispatched = true;
+
+ updateCaptureState(event);
+
+ if (mFallbackReceiver != null) {
+ View target = mFallbackReceiver.get();
+ if (mCapturedKeys.size() == 0) {
+ mFallbackReceiver = null;
+ }
+ if (target != null && target.isAttachedToWindow()) {
+ return target.onKeyFallback(event);
+ }
+ // consume anyways so that we don't feed uncaptured key events to other views
+ return true;
+ }
+
+ boolean result = dispatchInZOrder(root, event);
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ return result;
+ }
+
+ private boolean dispatchInZOrder(View view, KeyEvent evt) {
+ if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+ ArrayList<View> orderedViews = vg.buildOrderedChildList();
+ if (orderedViews != null) {
+ try {
+ for (int i = orderedViews.size() - 1; i >= 0; --i) {
+ View v = orderedViews.get(i);
+ if (dispatchInZOrder(v, evt)) {
+ return true;
+ }
+ }
+ } finally {
+ orderedViews.clear();
+ }
+ } else {
+ for (int i = vg.getChildCount() - 1; i >= 0; --i) {
+ View v = vg.getChildAt(i);
+ if (dispatchInZOrder(v, evt)) {
+ return true;
+ }
+ }
+ }
+ }
+ if (view.onKeyFallback(evt)) {
+ mFallbackReceiver = new WeakReference<>(view);
+ return true;
+ }
+ return false;
+ }
+
+ boolean hasFocus() {
+ return mFallbackReceiver != null;
+ }
+
+ boolean dispatchUnique(View root, KeyEvent event) {
+ if (mDispatched) {
+ return false;
+ }
+ return dispatch(root, event);
+ }
+ }
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 534335bf4743..c192f5c25f01 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1663,14 +1663,6 @@ public interface WindowManagerPolicy {
void writeToProto(ProtoOutputStream proto, long fieldId);
/**
- * Returns whether a given window type can be magnified.
- *
- * @param windowType The window type.
- * @return True if the window can be magnified.
- */
- public boolean canMagnifyWindow(int windowType);
-
- /**
* Returns whether a given window type is considered a top level one.
* A top level window does not have a container, i.e. attached window,
* or if it has a container it is laid out as a top-level window, not
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 4cf34618a0b1..63519a655113 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,6 +34,8 @@ public class UserPackage {
private final UserInfo mUserInfo;
private final PackageInfo mPackageInfo;
+ public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.O_MR1;
+
public UserPackage(UserInfo user, PackageInfo packageInfo) {
this.mUserInfo = user;
this.mPackageInfo = packageInfo;
@@ -83,7 +85,7 @@ public class UserPackage {
* supported by the current framework version.
*/
public static boolean hasCorrectTargetSdkVersion(PackageInfo packageInfo) {
- return packageInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O_MR1;
+ return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_SDK;
}
public UserInfo getUserInfo() {
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 203de9c2e7e0..e49373923ec9 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -29,10 +29,10 @@ import java.lang.annotation.Target;
/**
* Manages settings state for a WebView. When a WebView is first created, it
* obtains a set of default settings. These default settings will be returned
- * from any getter call. A WebSettings object obtained from
- * WebView.getSettings() is tied to the life of the WebView. If a WebView has
- * been destroyed, any method call on WebSettings will throw an
- * IllegalStateException.
+ * from any getter call. A {@code WebSettings} object obtained from
+ * {@link WebView#getSettings()} is tied to the life of the WebView. If a WebView has
+ * been destroyed, any method call on {@code WebSettings} will throw an
+ * {@link IllegalStateException}.
*/
// This is an abstract base class: concrete WebViewProviders must
// create a class derived from this, and return an instance of it in the
@@ -41,13 +41,13 @@ public abstract class WebSettings {
/**
* Enum for controlling the layout of html.
* <ul>
- * <li>NORMAL means no rendering changes. This is the recommended choice for maximum
+ * <li>{@code NORMAL} means no rendering changes. This is the recommended choice for maximum
* compatibility across different platforms and Android versions.</li>
- * <li>SINGLE_COLUMN moves all content into one column that is the width of the
+ * <li>{@code SINGLE_COLUMN} moves all content into one column that is the width of the
* view.</li>
- * <li>NARROW_COLUMNS makes all columns no wider than the screen if possible. Only use
+ * <li>{@code NARROW_COLUMNS} makes all columns no wider than the screen if possible. Only use
* this for API levels prior to {@link android.os.Build.VERSION_CODES#KITKAT}.</li>
- * <li>TEXT_AUTOSIZING boosts font size of paragraphs based on heuristics to make
+ * <li>{@code TEXT_AUTOSIZING} boosts font size of paragraphs based on heuristics to make
* the text readable when viewing a wide-viewport layout in the overview mode.
* It is recommended to enable zoom support {@link #setSupportZoom} when
* using this mode. Supported from API level
@@ -98,9 +98,9 @@ public abstract class WebSettings {
/**
* Enum for specifying the WebView's desired density.
* <ul>
- * <li>FAR makes 100% looking like in 240dpi</li>
- * <li>MEDIUM makes 100% looking like in 160dpi</li>
- * <li>CLOSE makes 100% looking like in 120dpi</li>
+ * <li>{@code FAR} makes 100% looking like in 240dpi</li>
+ * <li>{@code MEDIUM} makes 100% looking like in 160dpi</li>
+ * <li>{@code CLOSE} makes 100% looking like in 120dpi</li>
* </ul>
*/
public enum ZoomDensity {
@@ -652,7 +652,7 @@ public abstract class WebSettings {
* true, {@link WebChromeClient#onCreateWindow} must be implemented by the
* host application. The default is {@code false}.
*
- * @param support whether to suport multiple windows
+ * @param support whether to support multiple windows
*/
public abstract void setSupportMultipleWindows(boolean support);
@@ -665,7 +665,7 @@ public abstract class WebSettings {
public abstract boolean supportMultipleWindows();
/**
- * Sets the underlying layout algorithm. This will cause a relayout of the
+ * Sets the underlying layout algorithm. This will cause a re-layout of the
* WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}.
*
* @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value
@@ -1198,7 +1198,7 @@ public abstract class WebSettings {
/**
* Tells JavaScript to open windows automatically. This applies to the
- * JavaScript function window.open(). The default is {@code false}.
+ * JavaScript function {@code window.open()}. The default is {@code false}.
*
* @param flag {@code true} if JavaScript can open windows automatically
*/
@@ -1208,7 +1208,7 @@ public abstract class WebSettings {
* Gets whether JavaScript can open windows automatically.
*
* @return {@code true} if JavaScript can open windows automatically during
- * window.open()
+ * {@code window.open()}
* @see #setJavaScriptCanOpenWindowsAutomatically
*/
public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d477ffdf8b2b..d0c2d869b542 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -195,6 +195,27 @@ public class Editor {
private final boolean mHapticTextHandleEnabled;
private final Magnifier mMagnifier;
+ private final Runnable mUpdateMagnifierRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mMagnifier.update();
+ }
+ };
+ // Update the magnifier contents whenever anything in the view hierarchy is updated.
+ // Note: this only captures UI thread-visible changes, so it's a known issue that an animating
+ // VectorDrawable or Ripple animation will not trigger capture, since they're owned by
+ // RenderThread.
+ private final ViewTreeObserver.OnDrawListener mMagnifierOnDrawListener =
+ new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ if (mMagnifier != null) {
+ // Posting the method will ensure that updating the magnifier contents will
+ // happen right after the rendering of the current frame.
+ mTextView.post(mUpdateMagnifierRunnable);
+ }
+ }
+ };
// Used to highlight a word when it is corrected by the IME
private CorrectionHighlighter mCorrectionHighlighter;
@@ -415,15 +436,21 @@ public class Editor {
}
final ViewTreeObserver observer = mTextView.getViewTreeObserver();
- // No need to create the controller.
- // The get method will add the listener on controller creation.
- if (mInsertionPointCursorController != null) {
- observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
- }
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.resetTouchOffsets();
- observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+ if (observer.isAlive()) {
+ // No need to create the controller.
+ // The get method will add the listener on controller creation.
+ if (mInsertionPointCursorController != null) {
+ observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+ }
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.resetTouchOffsets();
+ observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+ }
+ if (FLAG_USE_MAGNIFIER) {
+ observer.addOnDrawListener(mMagnifierOnDrawListener);
+ }
}
+
updateSpellCheckSpans(0, mTextView.getText().length(),
true /* create the spell checker if needed */);
@@ -472,6 +499,13 @@ public class Editor {
mSpellChecker = null;
}
+ if (FLAG_USE_MAGNIFIER) {
+ final ViewTreeObserver observer = mTextView.getViewTreeObserver();
+ if (observer.isAlive()) {
+ observer.removeOnDrawListener(mMagnifierOnDrawListener);
+ }
+ }
+
hideCursorAndSpanControllers();
stopTextActionModeWithPreservingSelection();
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 85251d4bfcfc..5fddfba632c5 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -431,7 +431,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
}
- return super.dispatchKeyEvent(event);
+ if (super.dispatchKeyEvent(event)) {
+ return true;
+ }
+
+ return (getViewRootImpl() != null) && getViewRootImpl().dispatchKeyFallbackEvent(event);
}
public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 91bc6813c5fc..22bfcc347c3a 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -30,6 +30,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -134,6 +135,13 @@ public class ArrayUtils {
}
/**
+ * Checks if given map is null or has zero elements.
+ */
+ public static boolean isEmpty(@Nullable Map<?, ?> map) {
+ return map == null || map.isEmpty();
+ }
+
+ /**
* Checks if given array is null or has zero elements.
*/
public static <T> boolean isEmpty(@Nullable T[] array) {
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
index f6741c3b5169..f79a7f519b74 100644
--- a/core/java/com/android/internal/widget/Magnifier.java
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -18,34 +18,33 @@ package com.android.internal.widget;
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UiThread;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
-import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.PixelCopy;
+import android.view.Surface;
+import android.view.SurfaceView;
import android.view.View;
-import android.view.ViewRootImpl;
import android.widget.ImageView;
import android.widget.PopupWindow;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
-import java.util.Timer;
-import java.util.TimerTask;
-
/**
* Android magnifier widget. Can be used by any view which is attached to window.
*/
public final class Magnifier {
- private static final String LOG_TAG = "magnifier";
- private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps
- // The view for which this magnifier is attached.
+ // Use this to specify that a previous configuration value does not exist.
+ private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+ // The view to which this magnifier is attached.
private final View mView;
// The window containing the magnifier.
private final PopupWindow mWindow;
@@ -64,8 +63,12 @@ public final class Magnifier {
private final Handler mPixelCopyHandler = Handler.getMain();
// Current magnification scale.
private final float mZoomScale;
- // Timer used to schedule the copy task.
- private Timer mTimer;
+ // Variables holding previous states, used for detecting redundant calls and invalidation.
+ private final Point mPrevStartCoordsInSurface = new Point(
+ NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+ private final PointF mPrevPosInView = new PointF(
+ NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+ private final Rect mPixelCopyRequestRect = new Rect();
/**
* Initializes a magnifier.
@@ -91,8 +94,8 @@ public final class Magnifier {
mWindow.setTouchable(false);
mWindow.setBackgroundDrawable(null);
- final int bitmapWidth = (int) (mWindowWidth / mZoomScale);
- final int bitmapHeight = (int) (mWindowHeight / mZoomScale);
+ final int bitmapWidth = Math.round(mWindowWidth / mZoomScale);
+ final int bitmapHeight = Math.round(mWindowHeight / mZoomScale);
mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
getImageView().setImageBitmap(mBitmap);
}
@@ -106,32 +109,29 @@ public final class Magnifier {
* relative to the view. The lower end is clamped to 0
*/
public void show(@FloatRange(from=0) float xPosInView, @FloatRange(from=0) float yPosInView) {
- if (xPosInView < 0) {
- xPosInView = 0;
- }
-
- if (yPosInView < 0) {
- yPosInView = 0;
- }
+ xPosInView = Math.max(0, xPosInView);
+ yPosInView = Math.max(0, yPosInView);
configureCoordinates(xPosInView, yPosInView);
- if (mTimer == null) {
- mTimer = new Timer();
- mTimer.schedule(new TimerTask() {
- @Override
- public void run() {
- performPixelCopy();
- }
- }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS);
- }
+ // Clamp startX value to avoid distorting the rendering of the magnifier content.
+ final int startX = Math.max(0, Math.min(
+ mCenterZoomCoords.x - mBitmap.getWidth() / 2,
+ mView.getWidth() - mBitmap.getWidth()));
+ final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
- if (mWindow.isShowing()) {
- mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
- mWindow.getHeight());
- } else {
- mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
- mWindowCoords.x, mWindowCoords.y);
+ if (startX != mPrevStartCoordsInSurface.x || startY != mPrevStartCoordsInSurface.y) {
+ performPixelCopy(startX, startY);
+
+ mPrevPosInView.x = xPosInView;
+ mPrevPosInView.y = yPosInView;
+
+ if (mWindow.isShowing()) {
+ mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
+ mWindow.getHeight());
+ } else {
+ mWindow.showAtLocation(mView, Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y);
+ }
}
}
@@ -140,11 +140,18 @@ public final class Magnifier {
*/
public void dismiss() {
mWindow.dismiss();
+ }
- if (mTimer != null) {
- mTimer.cancel();
- mTimer.purge();
- mTimer = null;
+ /**
+ * Forces the magnifier to update its content. It uses the previous coordinates passed to
+ * {@link #show(float, float)}. This only happens if the magnifier is currently showing.
+ *
+ * @hide
+ */
+ public void update() {
+ if (mWindow.isShowing()) {
+ // Update the contents shown in the magnifier.
+ performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y);
}
}
@@ -170,13 +177,22 @@ public final class Magnifier {
}
private void configureCoordinates(float xPosInView, float yPosInView) {
- final int[] coordinatesOnScreen = new int[2];
- mView.getLocationOnScreen(coordinatesOnScreen);
- final float posXOnScreen = xPosInView + coordinatesOnScreen[0];
- final float posYOnScreen = yPosInView + coordinatesOnScreen[1];
+ final float posX;
+ final float posY;
- mCenterZoomCoords.x = (int) posXOnScreen;
- mCenterZoomCoords.y = (int) posYOnScreen;
+ if (mView instanceof SurfaceView) {
+ // No offset required if the backing Surface matches the size of the SurfaceView.
+ posX = xPosInView;
+ posY = yPosInView;
+ } else {
+ final int[] coordinatesInSurface = new int[2];
+ mView.getLocationInSurface(coordinatesInSurface);
+ posX = xPosInView + coordinatesInSurface[0];
+ posY = yPosInView + coordinatesInSurface[1];
+ }
+
+ mCenterZoomCoords.x = Math.round(posX);
+ mCenterZoomCoords.y = Math.round(posY);
final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize(
R.dimen.magnifier_offset);
@@ -184,32 +200,34 @@ public final class Magnifier {
mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
}
- private void performPixelCopy() {
- final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
- int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
-
- // Clamp startX value to avoid distorting the rendering of the magnifier content.
- if (rawStartX < 0) {
- rawStartX = 0;
- } else if (rawStartX + mBitmap.getWidth() > mView.getWidth()) {
- rawStartX = mView.getWidth() - mBitmap.getWidth();
+ private void performPixelCopy(final int startXInSurface, final int startYInSurface) {
+ final Surface surface = getValidViewSurface();
+ if (surface != null) {
+ mPixelCopyRequestRect.set(startXInSurface, startYInSurface,
+ startXInSurface + mBitmap.getWidth(), startYInSurface + mBitmap.getHeight());
+
+ PixelCopy.request(surface, mPixelCopyRequestRect, mBitmap,
+ result -> {
+ getImageView().invalidate();
+ mPrevStartCoordsInSurface.x = startXInSurface;
+ mPrevStartCoordsInSurface.y = startYInSurface;
+ },
+ mPixelCopyHandler);
}
+ }
- final int startX = rawStartX;
- final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
-
- if (viewRootImpl != null && viewRootImpl.mSurface != null
- && viewRootImpl.mSurface.isValid()) {
- PixelCopy.request(
- viewRootImpl.mSurface,
- new Rect(startX, startY, startX + mBitmap.getWidth(),
- startY + mBitmap.getHeight()),
- mBitmap,
- result -> getImageView().invalidate(),
- mPixelCopyHandler);
+ @Nullable
+ private Surface getValidViewSurface() {
+ final Surface surface;
+ if (mView instanceof SurfaceView) {
+ surface = ((SurfaceView) mView).getHolder().getSurface();
+ } else if (mView.getViewRootImpl() != null) {
+ surface = mView.getViewRootImpl().mSurface;
} else {
- Log.d(LOG_TAG, "Could not perform PixelCopy request");
+ surface = null;
}
+
+ return (surface != null && surface.isValid()) ? surface : null;
}
private ImageView getImageView() {
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 7abc76a85af5..408a4e9b02a4 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -9556,7 +9556,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
if (vScroll == 0 && hScroll == 0) {
return false;
}
- mRecyclerView.scrollBy(hScroll, vScroll);
+ mRecyclerView.smoothScrollBy(hScroll, vScroll);
return true;
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3552e43b682a..9d036dc07835 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,7 +42,6 @@ cc_library_shared {
"com_google_android_gles_jni_EGLImpl.cpp",
"com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
"android_app_Activity.cpp",
- "android_app_ApplicationLoaders.cpp",
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
"android_opengl_EGL14.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 897789176719..2a65cde934d8 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -187,7 +187,6 @@ extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
extern int register_android_app_backup_FullBackup(JNIEnv *env);
extern int register_android_app_Activity(JNIEnv *env);
extern int register_android_app_ActivityThread(JNIEnv *env);
-extern int register_android_app_ApplicationLoaders(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_media_RemoteDisplay(JNIEnv *env);
extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
@@ -1456,7 +1455,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_app_backup_FullBackup),
REG_JNI(register_android_app_Activity),
REG_JNI(register_android_app_ActivityThread),
- REG_JNI(register_android_app_ApplicationLoaders),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_util_jar_StrictJarFile),
REG_JNI(register_android_view_InputChannel),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 1a19a4099e12..dd3e6f02e9fe 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,10 +44,10 @@ namespace android {
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
- : langId(langId), variant(static_cast<minikin::FontVariant>(variant)),
+ : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
allowUnsupportedFont(false) {}
uint32_t langId;
- minikin::FontVariant variant;
+ minikin::FontFamily::Variant variant;
bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
@@ -141,7 +141,7 @@ static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, in
}
builder->fonts.push_back(minikin::Font(minikinFont,
- minikin::FontStyle(weight, static_cast<minikin::FontSlant>(italic))));
+ minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
builder->axes.clear();
return true;
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 5f32d379af24..1e7f5f5c1022 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -581,7 +581,7 @@ namespace PaintGlue {
// restore the original settings.
paint->setTextSkewX(saveSkewX);
paint->setFakeBoldText(savefakeBold);
- if (paint->getFontVariant() == minikin::FontVariant::ELEGANT) {
+ if (paint->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT) {
SkScalar size = paint->getTextSize();
metrics->fTop = -size * kElegantTop / 2048;
metrics->fBottom = -size * kElegantBottom / 2048;
@@ -880,12 +880,13 @@ namespace PaintGlue {
static jboolean isElegantTextHeight(jlong paintHandle) {
Paint* obj = reinterpret_cast<Paint*>(paintHandle);
- return obj->getFontVariant() == minikin::FontVariant::ELEGANT;
+ return obj->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT;
}
static void setElegantTextHeight(jlong paintHandle, jboolean aa) {
Paint* obj = reinterpret_cast<Paint*>(paintHandle);
- obj->setFontVariant(aa ? minikin::FontVariant::ELEGANT : minikin::FontVariant::DEFAULT);
+ obj->setFamilyVariant(
+ aa ? minikin::FontFamily::Variant::ELEGANT : minikin::FontFamily::Variant::DEFAULT);
}
static jfloat getTextSize(jlong paintHandle) {
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 3e4073f6f215..d67c0b018efb 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -83,7 +83,7 @@ static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) {
static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
- return face->fStyle.weight;
+ return face->fStyle.weight();
}
static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp
deleted file mode 100644
index 8bbf24a606e4..000000000000
--- a/core/jni/android_app_ApplicationLoaders.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ApplicationLoaders"
-
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativeloader/native_loader.h>
-#include <vulkan/vulkan_loader_data.h>
-
-#include "core_jni_helpers.h"
-
-static void setupVulkanLayerPath_native(JNIEnv* env, jobject clazz,
- jobject classLoader, jstring librarySearchPath) {
- android_namespace_t* ns = android::FindNamespaceByClassLoader(env, classLoader);
- ScopedUtfChars layerPathChars(env, librarySearchPath);
-
- vulkan::LoaderData& loader_data = vulkan::LoaderData::GetInstance();
- if (loader_data.layer_path.empty()) {
- loader_data.layer_path = layerPathChars.c_str();
- loader_data.app_namespace = ns;
- } else {
- ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
- layerPathChars.c_str(), ns);
- }
-}
-
-static const JNINativeMethod g_methods[] = {
- { "setupVulkanLayerPath", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
- reinterpret_cast<void*>(setupVulkanLayerPath_native) },
-};
-
-static const char* const kApplicationLoadersName = "android/app/ApplicationLoaders";
-
-namespace android
-{
-
-int register_android_app_ApplicationLoaders(JNIEnv* env) {
- return RegisterMethodsOrDie(env, kApplicationLoadersName, g_methods, NELEM(g_methods));
-}
-
-} // namespace android
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index f749488f39b0..4ecfd4bdc217 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -18,6 +18,7 @@
#include <graphicsenv/GraphicsEnv.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <nativeloader/native_loader.h>
#include "core_jni_helpers.h"
namespace {
@@ -27,8 +28,23 @@ void setDriverPath(JNIEnv* env, jobject clazz, jstring path) {
android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
}
+void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
+ android_namespace_t* appNamespace = android::FindNamespaceByClassLoader(env, classLoader);
+ ScopedUtfChars layerPathsChars(env, layerPaths);
+ android::GraphicsEnv::getInstance().setLayerPaths(appNamespace, layerPathsChars.c_str());
+}
+
+void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) {
+ if (layers != nullptr) {
+ ScopedUtfChars layersChars(env, layers);
+ android::GraphicsEnv::getInstance().setDebugLayers(layersChars.c_str());
+ }
+}
+
const JNINativeMethod g_methods[] = {
{ "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+ { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
+ { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
};
const char* const kGraphicsEnvironmentName = "android/os/GraphicsEnvironment";
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 4e88a83a92ea..d7300c4947d9 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -36,7 +36,7 @@
#include <hwui/MinikinUtils.h>
#include <hwui/Paint.h>
#include <minikin/FontCollection.h>
-#include <minikin/LineBreaker.h>
+#include <minikin/AndroidLineBreakerHelper.h>
#include <minikin/MinikinFont.h>
namespace android {
@@ -52,63 +52,6 @@ struct JLineBreaksID {
static jclass gLineBreaks_class;
static JLineBreaksID gLineBreaks_fieldID;
-class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate {
- public:
- JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth,
- const std::vector<float>& indents, const std::vector<float>& leftPaddings,
- const std::vector<float>& rightPaddings, int32_t indentsAndPaddingsOffset)
- : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
- mIndents(indents), mLeftPaddings(leftPaddings),
- mRightPaddings(rightPaddings), mOffset(indentsAndPaddingsOffset) {}
-
- float getLineWidth(size_t lineNo) override {
- const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount)
- ? mFirstWidth : mRestWidth;
- return width - get(mIndents, lineNo);
- }
-
- float getMinLineWidth() override {
- // A simpler algorithm would have been simply looping until the larger of
- // mFirstLineCount and mIndents.size()-mOffset, but that does unnecessary calculations
- // when mFirstLineCount is large. Instead, we measure the first line, all the lines that
- // have an indent, and the first line after firstWidth ends and restWidth starts.
- float minWidth = std::min(getLineWidth(0), getLineWidth(mFirstLineCount));
- for (size_t lineNo = 1; lineNo + mOffset < mIndents.size(); lineNo++) {
- minWidth = std::min(minWidth, getLineWidth(lineNo));
- }
- return minWidth;
- }
-
- float getLeftPadding(size_t lineNo) override {
- return get(mLeftPaddings, lineNo);
- }
-
- float getRightPadding(size_t lineNo) override {
- return get(mRightPaddings, lineNo);
- }
-
- private:
- float get(const std::vector<float>& vec, size_t lineNo) {
- if (vec.empty()) {
- return 0;
- }
- const size_t index = lineNo + mOffset;
- if (index < vec.size()) {
- return vec[index];
- } else {
- return vec.back();
- }
- }
-
- const float mFirstWidth;
- const int32_t mFirstLineCount;
- const float mRestWidth;
- const std::vector<float>& mIndents;
- const std::vector<float>& mLeftPaddings;
- const std::vector<float>& mRightPaddings;
- const int32_t mOffset;
-};
-
static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
if (javaArray == nullptr) {
return std::vector<float>();
@@ -118,109 +61,8 @@ static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray j
}
}
-class Run {
- public:
- Run(int32_t start, int32_t end) : mRange(start, end) {}
- virtual ~Run() {}
-
- virtual void addTo(minikin::LineBreaker* lineBreaker) = 0;
-
- protected:
- minikin::Range mRange;
-
- private:
- // Forbid copy and assign.
- Run(const Run&) = delete;
- void operator=(const Run&) = delete;
-};
-
-class StyleRun : public Run {
- public:
- StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
- std::shared_ptr<minikin::FontCollection>&& collection, bool isRtl)
- : Run(start, end), mPaint(std::move(paint)), mCollection(std::move(collection)),
- mIsRtl(isRtl) {}
-
- void addTo(minikin::LineBreaker* lineBreaker) override {
- lineBreaker->addStyleRun(&mPaint, mCollection, mRange, mIsRtl);
- }
-
- private:
- minikin::MinikinPaint mPaint;
- std::shared_ptr<minikin::FontCollection> mCollection;
- const bool mIsRtl;
-};
-
-class Replacement : public Run {
- public:
- Replacement(int32_t start, int32_t end, float width, uint32_t localeListId)
- : Run(start, end), mWidth(width), mLocaleListId(localeListId) {}
-
- void addTo(minikin::LineBreaker* lineBreaker) override {
- lineBreaker->addReplacement(mRange, mWidth, mLocaleListId);
- }
-
- private:
- const float mWidth;
- const uint32_t mLocaleListId;
-};
-
-class StaticLayoutNative {
- public:
- StaticLayoutNative(
- minikin::BreakStrategy strategy, minikin::HyphenationFrequency frequency,
- bool isJustified, std::vector<float>&& indents, std::vector<float>&& leftPaddings,
- std::vector<float>&& rightPaddings)
- : mStrategy(strategy), mFrequency(frequency), mIsJustified(isJustified),
- mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
- mRightPaddings(std::move(rightPaddings)) {}
-
- void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
- std::shared_ptr<minikin::FontCollection> collection, bool isRtl) {
- mRuns.emplace_back(std::make_unique<StyleRun>(
- start, end, std::move(paint), std::move(collection), isRtl));
- }
-
- void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
- mRuns.emplace_back(std::make_unique<Replacement>(start, end, width, localeListId));
- }
-
- // Only valid while this instance is alive.
- inline std::unique_ptr<minikin::LineBreaker::LineWidthDelegate> buildLineWidthDelegate(
- float firstWidth, int32_t firstLineCount, float restWidth,
- int32_t indentsAndPaddingsOffset) {
- return std::make_unique<JNILineBreakerLineWidth>(
- firstWidth, firstLineCount, restWidth, mIndents, mLeftPaddings, mRightPaddings,
- indentsAndPaddingsOffset);
- }
-
- void addRuns(minikin::LineBreaker* lineBreaker) {
- for (const auto& run : mRuns) {
- run->addTo(lineBreaker);
- }
- }
-
- void clearRuns() {
- mRuns.clear();
- }
-
- inline minikin::BreakStrategy getStrategy() const { return mStrategy; }
- inline minikin::HyphenationFrequency getFrequency() const { return mFrequency; }
- inline bool isJustified() const { return mIsJustified; }
-
- private:
- const minikin::BreakStrategy mStrategy;
- const minikin::HyphenationFrequency mFrequency;
- const bool mIsJustified;
- const std::vector<float> mIndents;
- const std::vector<float> mLeftPaddings;
- const std::vector<float> mRightPaddings;
-
- std::vector<std::unique_ptr<Run>> mRuns;
-};
-
-static inline StaticLayoutNative* toNative(jlong ptr) {
- return reinterpret_cast<StaticLayoutNative*>(ptr);
+static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
+ return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
}
// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
@@ -228,7 +70,7 @@ static inline StaticLayoutNative* toNative(jlong ptr) {
static jlong nInit(JNIEnv* env, jclass /* unused */,
jint breakStrategy, jint hyphenationFrequency, jboolean isJustified,
jintArray indents, jintArray leftPaddings, jintArray rightPaddings) {
- return reinterpret_cast<jlong>(new StaticLayoutNative(
+ return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
static_cast<minikin::BreakStrategy>(breakStrategy),
static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
isJustified,
@@ -291,7 +133,7 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
jintArray recycleFlags,
jfloatArray charWidths) {
- StaticLayoutNative* builder = toNative(nativePtr);
+ minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
ScopedCharArrayRO text(env, javaText);
@@ -327,7 +169,7 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
// Basically similar to Paint.getTextRunAdvances but with C++ interface
// CriticalNative
static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) {
- StaticLayoutNative* builder = toNative(nativePtr);
+ minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
@@ -337,7 +179,7 @@ static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint en
// CriticalNative
static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end,
jfloat width) {
- StaticLayoutNative* builder = toNative(nativePtr);
+ minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
builder->addReplacementRun(start, end, width, paint->getMinikinLocaleListId());
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cfeba833a5a3..bb1bfad26dc3 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -311,6 +311,14 @@ static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionO
transaction->apply(sync);
}
+static void nativeMergeTransaction(JNIEnv* env, jclass clazz,
+ jlong transactionObj, jlong otherTransactionObj) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ auto otherTransaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(
+ otherTransactionObj);
+ transaction->merge(std::move(*otherTransaction));
+}
+
static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz, jlong transactionObj) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->setAnimationTransaction();
@@ -882,6 +890,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeApplyTransaction },
{"nativeGetNativeTransactionFinalizer", "()J",
(void*)nativeGetNativeTransactionFinalizer },
+ {"nativeMergeTransaction", "(JJ)V",
+ (void*)nativeMergeTransaction },
{"nativeSetAnimationTransaction", "(J)V",
(void*)nativeSetAnimationTransaction },
{"nativeSetLayer", "(JJI)V",
diff --git a/core/proto/android/app/notification.proto b/core/proto/android/app/notification.proto
new file mode 100644
index 000000000000..5376b0ed5c4c
--- /dev/null
+++ b/core/proto/android/app/notification.proto
@@ -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.
+ */
+
+syntax = "proto2";
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.Notification object.
+ * Deprecated fields are not included in the proto.
+ */
+message NotificationProto {
+ optional string channel_id = 1;
+ optional bool has_ticker_text = 2;
+ optional int32 flags = 3;
+ optional int32 color = 4;
+ optional string category = 5;
+ optional string group_key = 6;
+ optional string sort_key = 7;
+ optional int32 action_length = 8;
+
+ // If this field is not set, then the value is unknown.
+ enum Visibility {
+ VISIBILITY_SECRET = -1;
+ VISIBILITY_PRIVATE = 0;
+ VISIBILITY_PUBLIC = 1;
+ }
+ optional Visibility visibility = 9;
+ optional NotificationProto public_version = 10;
+}
diff --git a/core/proto/android/os/cpufreq.proto b/core/proto/android/os/cpufreq.proto
new file mode 100644
index 000000000000..a8da0bf2f849
--- /dev/null
+++ b/core/proto/android/os/cpufreq.proto
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuFreqProto";
+
+package android.os;
+
+// cpu frequency time from /sys/devices/system/cpu/cpufreq/all_time_in_state
+message CpuFreq {
+
+ optional int32 jiffy_hz = 1; // obtain by system config.
+
+ repeated CpuFreqStats cpu_freqs = 2;
+}
+
+// frequency time pre cpu, unit in jiffy, TODO: obtain jiffies.
+message CpuFreqStats {
+
+ optional string cpu_name = 1;
+
+ message TimeInState {
+ optional int32 state_khz = 1; // cpu frequency
+ optional int64 time_jiffy = 2; // number of jiffies
+ }
+ repeated TimeInState times = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index f68f3a4ad7a4..ecdabcf98f39 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,6 +20,7 @@ option java_outer_classname = "IncidentProtoMetadata";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
+import "frameworks/base/core/proto/android/os/cpufreq.proto";
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -78,6 +79,10 @@ message IncidentProto {
(section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
];
+ optional CpuFreq cpu_freq = 2004 [
+ (section).type = SECTION_FILE,
+ (section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
+ ];
// System Services
optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
@@ -143,7 +148,11 @@ message IncidentProto {
(section).args = "activity --proto broadcasts"
];
- optional com.android.server.am.proto.ServiceProto amservices = 3014;
+ optional com.android.server.am.proto.ActiveServicesProto amservices = 3014 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "activity --proto service"
+ ];
+
optional com.android.server.am.proto.ProcessProto amprocesses = 3015;
optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 764288c51b66..5d5aea2a12cd 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -305,6 +305,9 @@ message GlobalSettingsProto {
optional SettingProto preferred_network_mode = 226;
optional SettingProto debug_app = 227;
optional SettingProto wait_for_debugger = 228;
+ optional SettingProto enable_gpu_debug_layers = 342;
+ optional SettingProto gpu_debug_app = 343;
+ optional SettingProto gpu_debug_layers = 344;
optional SettingProto low_power_mode = 229;
optional SettingProto low_power_mode_trigger_level = 230;
optional SettingProto always_finish_activities = 231;
@@ -384,7 +387,7 @@ message GlobalSettingsProto {
optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
optional SettingProto notification_snooze_options = 341;
- // Next tag = 342;
+ // Next tag = 345;
}
message SecureSettingsProto {
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index c57cb72c8b68..889842ccc99e 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -15,11 +15,14 @@
*/
syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/notification.proto";
import "frameworks/base/core/proto/android/content/intent.proto";
import "frameworks/base/core/proto/android/server/intentresolver.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/graphics/rect.proto";
import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/util/common.proto";
package com.android.server.am.proto;
@@ -30,11 +33,12 @@ message ActivityManagerServiceProto {
optional BroadcastProto broadcasts = 2;
- optional ServiceProto services = 3;
+ optional ActiveServicesProto services = 3;
optional ProcessProto processes = 4;
}
+// "dumpsys activity --proto activities"
message ActivityStackSupervisorProto {
optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
repeated ActivityDisplayProto displays = 2;
@@ -90,6 +94,7 @@ message KeyguardControllerProto {
optional bool keyguard_occluded = 2;
}
+// "dumpsys activity --proto broadcasts"
message BroadcastProto {
repeated ReceiverListProto receiver_list = 1;
@@ -164,10 +169,158 @@ message StickyBroadcastProto {
repeated StickyAction actions = 2;
}
-message ServiceProto {
- // TODO: "dumpsys activity --proto services"
+// "dumpsys activity --proto service"
+message ActiveServicesProto {
+
+ message ServicesByUser {
+ optional int32 user_id = 1;
+ repeated ServiceRecordProto service_records = 2;
+ }
+ repeated ServicesByUser services_by_users = 1;
+}
+
+// corresponds to ActivityManagerService.GrantUri Java class
+message GrantUriProto {
+ optional int32 source_user_id = 1;
+ optional string uri = 2;
+}
+
+message NeededUriGrantsProto {
+ optional string target_package = 1;
+ optional int32 target_uid = 2;
+ optional int32 flags = 3;
+
+ repeated GrantUriProto grants = 4;
+}
+
+message UriPermissionOwnerProto {
+ optional string owner = 1;
+ repeated GrantUriProto read_perms = 2;
+ repeated GrantUriProto write_perms = 3;
+}
+
+message ServiceRecordProto {
+ optional string short_name = 1;
+ optional string hex_hash = 2;
+ optional bool is_running = 3; // false if the application service is null
+ optional int32 pid = 4;
+ optional .android.content.IntentProto intent = 5;
+ optional string package_name = 6;
+ optional string process_name = 7;
+ optional string permission = 8;
+
+ message AppInfo {
+ optional string base_dir = 1;
+ optional string res_dir = 2;
+ optional string data_dir = 3;
+ }
+ optional AppInfo appinfo = 9;
+ optional ProcessRecordProto app = 10;
+ optional ProcessRecordProto isolated_proc = 11;
+ optional bool whitelist_manager = 12;
+ optional bool delayed = 13;
+
+ message Foreground {
+ optional int32 id = 1;
+ optional .android.app.NotificationProto notification = 2;
+ }
+ optional Foreground foreground = 14;
+
+ optional .android.util.Duration create_real_time = 15;
+ optional .android.util.Duration starting_bg_timeout = 16;
+ optional .android.util.Duration last_activity_time = 17;
+ optional .android.util.Duration restart_time = 18;
+ optional bool created_from_fg = 19;
+
+ // variables used to track states related to service start
+ message Start {
+ optional bool start_requested = 1;
+ optional bool delayed_stop = 2;
+ optional bool stop_if_killed = 3;
+ optional bool call_start = 4;
+ optional int32 last_start_id = 5;
+ }
+ optional Start start = 20;
+
+ message ExecuteNesting {
+ optional int32 execute_nesting = 1;
+ optional bool execute_fg = 2;
+ optional .android.util.Duration executing_start = 3;
+ }
+ optional ExecuteNesting execute = 21;
+
+ optional .android.util.Duration destory_time = 22;
+
+ message Crash {
+ optional int32 restart_count = 1;
+ optional .android.util.Duration restart_delay = 2;
+ optional .android.util.Duration next_restart_time = 3;
+ optional int32 crash_count = 4;
+ }
+ optional Crash crash = 23;
+
+ message StartItemProto {
+ optional int32 id = 1;
+ optional .android.util.Duration duration = 2;
+ optional int32 delivery_count = 3;
+ optional int32 done_executing_count = 4;
+ optional .android.content.IntentProto intent = 5;
+ optional NeededUriGrantsProto needed_grants = 6;
+ optional UriPermissionOwnerProto uri_permissions = 7;
+ }
+ repeated StartItemProto delivered_starts = 24;
+ repeated StartItemProto pending_starts = 25;
+
+ repeated IntentBindRecordProto bindings = 26;
+ repeated ConnectionRecordProto connections = 27;
+}
+
+message ConnectionRecordProto {
+ optional string hex_hash = 1;
+ optional int32 user_id = 2;
+
+ enum Flag {
+ AUTO_CREATE = 0;
+ DEBUG_UNBIND = 1;
+ NOT_FG = 2;
+ IMPORTANT_BG = 3;
+ ABOVE_CLIENT = 4;
+ ALLOW_OOM_MANAGEMENT = 5;
+ WAIVE_PRIORITY = 6;
+ IMPORTANT = 7;
+ ADJUST_WITH_ACTIVITY = 8;
+ FG_SERVICE_WHILE_WAKE = 9;
+ FG_SERVICE = 10;
+ TREAT_LIKE_ACTIVITY = 11;
+ VISIBLE = 12;
+ SHOWING_UI = 13;
+ NOT_VISIBLE = 14;
+ DEAD = 15;
+ }
+ repeated Flag flags = 3;
+ optional string service_name = 4;
+ optional string conn_hex_hash = 5;
+}
+
+message AppBindRecordProto {
+ optional string hex_hash = 1;
+ optional ProcessRecordProto client = 2;
+ repeated ConnectionRecordProto connections = 3;
+}
+
+message IntentBindRecordProto {
+ optional string hex_hash = 1;
+ optional bool is_create = 2;
+ optional .android.content.IntentProto intent = 3;
+ optional string binder = 4;
+ optional bool requested = 5;
+ optional bool received = 6;
+ optional bool has_bound = 7;
+ optional bool do_rebind = 8;
+
+ repeated AppBindRecordProto apps = 9;
}
+// TODO: "dumpsys activity --proto processes"
message ProcessProto {
- // TODO: "dumpsys activity --proto processes"
}
diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto
index 429c3cadc89a..308ef703bddb 100644
--- a/core/proto/android/util/common.proto
+++ b/core/proto/android/util/common.proto
@@ -30,3 +30,13 @@ message AggStats {
optional int64 max = 3;
}
+
+/**
+ * Very basic data structure to represent Duration.
+ */
+message Duration {
+
+ optional int64 start_ms = 1;
+
+ optional int64 end_ms = 2;
+} \ No newline at end of file
diff --git a/core/res/Android.mk b/core/res/Android.mk
index b0669294dd9d..370a01ff3b93 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -29,6 +29,9 @@ LOCAL_AAPT_FLAGS += --private-symbols com.android.internal
# Framework doesn't need versioning since it IS the platform.
LOCAL_AAPT_FLAGS += --no-auto-version
+# Allow overlay to add resource
+LOCAL_AAPT_FLAGS += --auto-add-overlay
+
# Install this alongside the libraries.
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index dddd52bd1ef2..cea9cd70f5ac 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -28,7 +28,7 @@
<string name="unknownName" msgid="6867811765370350269">"غير معروف"</string>
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"البريد الصوتي"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
- <string name="mmiError" msgid="5154499457739052907">"‏حدثت مشكلة في الاتصال أو أن كود MMI غير صحيح."</string>
+ <string name="mmiError" msgid="5154499457739052907">"‏حدثت مشكلة في الاتصال أو أن رمز MMI غير صحيح."</string>
<string name="mmiFdnError" msgid="5224398216385316471">"تم تقييد التشغيل لأرقام الاتصال الثابت فقط."</string>
<string name="mmiErrorWhileRoaming" msgid="762488890299284230">"يتعذر تغيير إعدادات إعادة توجيه المكالمات من هاتفك أثناء التجوال."</string>
<string name="serviceEnabled" msgid="8147278346414714315">"تم تمكين الخدمة."</string>
@@ -43,7 +43,7 @@
<string name="mismatchPin" msgid="609379054496863419">"أرقام التعريف الشخصية التي كتبتها غير مطابقة."</string>
<string name="invalidPin" msgid="3850018445187475377">"ادخل رقم تعريف شخصي مكون من ٤ إلى ٨ أرقام."</string>
<string name="invalidPuk" msgid="8761456210898036513">"‏اكتب رمز PUK مكونًا من ٨ أرقام أو أكثر."</string>
- <string name="needPuk" msgid="919668385956251611">"‏شريحة SIM مؤمّنة بكود PUK. اكتب كود PUK لإلغاء تأمينها."</string>
+ <string name="needPuk" msgid="919668385956251611">"‏شريحة SIM مؤمّنة برمز PUK. اكتب رمز PUK لإلغاء تأمينها."</string>
<string name="needPuk2" msgid="4526033371987193070">"‏اكتب PUK2 لإلغاء تأمين شريحة SIM."</string>
<string name="enablePin" msgid="209412020907207950">"‏محاولة غير ناجحة، مكّن قفل SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
@@ -139,8 +139,8 @@
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
- <string name="fcComplete" msgid="3118848230966886575">"اكتمل كود الميزة."</string>
- <string name="fcError" msgid="3327560126588500777">"حدثت مشكلة بالاتصال أو أن كود الميزة غير صحيح."</string>
+ <string name="fcComplete" msgid="3118848230966886575">"اكتمل رمز الميزة."</string>
+ <string name="fcError" msgid="3327560126588500777">"حدثت مشكلة بالاتصال أو أن رمز الميزة غير صحيح."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"حسنًا"</string>
<string name="httpError" msgid="7956392511146698522">"حدث خطأ في الشبكة."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"‏تعذر العثور على عنوان URL."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 05ab42d81569..658e9514df5f 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1169,7 +1169,7 @@
<string name="sim_done_button" msgid="827949989369963775">"Terminé"</string>
<string name="sim_added_title" msgid="3719670512889674693">"Carte SIM ajoutée."</string>
<string name="sim_added_message" msgid="6599945301141050216">"Redémarrez votre appareil pour accéder au réseau mobile."</string>
- <string name="sim_restart_button" msgid="4722407842815232347">"Recommencer"</string>
+ <string name="sim_restart_button" msgid="4722407842815232347">"Redémarrer"</string>
<string name="carrier_app_dialog_message" msgid="7066156088266319533">"Pour que la nouvelle carte SIM fonctionne correctement, vous devez installer et ouvrir une application fournie par votre fournisseur de services."</string>
<string name="carrier_app_dialog_button" msgid="7900235513678617329">"TÉLÉCHARGER L\'APPLICATION"</string>
<string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"PAS MAINTENANT"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index cea220a7256c..c866c4cfdfb2 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -172,7 +172,7 @@
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Wasifu wako wa kazini haupatikani tena kwenye kifaa hiki"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Umejaribu kuweka nenosiri mara nyingi mno"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Kifaa kinadhibitiwa"</string>
- <string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gonga ili upate maelezo zaidi."</string>
+ <string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gusa ili upate maelezo zaidi."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Data iliyomo kwenye kifaa chako itafutwa"</string>
<string name="factory_reset_message" msgid="7972496262232832457">"Huwezi kutumia programu ya msimamizi. Sasa data iliyo kwenye kifaa chako itafutwa.\n\nIkiwa una maswali yoyote, wasiliana na msimamizi wa shirika lako."</string>
<string name="me" msgid="6545696007631404292">"Mimi"</string>
@@ -251,7 +251,7 @@
<string name="notification_channel_foreground_service" msgid="3931987440602669158">"Programu zinazotumia betri"</string>
<string name="foreground_service_app_in_background" msgid="1060198778219731292">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumia betri"</string>
<string name="foreground_service_apps_in_background" msgid="7175032677643332242">"Programu <xliff:g id="NUMBER">%1$d</xliff:g> zinatumia betri"</string>
- <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Gonga ili upate maelezo kuhusu betri na matumizi ya data"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
<string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
@@ -807,7 +807,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Bakia kwenye Ukurasa huu"</string>
<string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nJe, una uhakika unataka kutoka kwenye ukurasa huu?"</string>
<string name="save_password_label" msgid="6860261758665825069">"Thibitisha"</string>
- <string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gonga mara mbili ili kukuza ndani na nje."</string>
+ <string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gusa mara mbili ili kukuza ndani na nje."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Kujaza kiotomatiki"</string>
<string name="setup_autofill" msgid="7103495070180590814">"Weka uwezo wa kujaza kiotomatiki"</string>
<string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
@@ -971,7 +971,7 @@
<string name="undo" msgid="7905788502491742328">"Tendua"</string>
<string name="redo" msgid="7759464876566803888">"Rejesha"</string>
<string name="autofill" msgid="3035779615680565188">"Kujaza kiotomatiki"</string>
- <string name="textSelectionCABTitle" msgid="5236850394370820357">"Uchaguzi wa maandishi?"</string>
+ <string name="textSelectionCABTitle" msgid="5236850394370820357">"Maandishi yaliyoteuliwa"</string>
<string name="addToDictionary" msgid="4352161534510057874">"Ongeza kwenye kamusi"</string>
<string name="deleteText" msgid="6979668428458199034">"Futa"</string>
<string name="inputMethod" msgid="1653630062304567879">"Mbinu ya uingizaji"</string>
@@ -986,7 +986,7 @@
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumiwa"</string>
- <string name="app_running_notification_text" msgid="1197581823314971177">"Gonga ili upate maelezo zaidi au usitishe programu."</string>
+ <string name="app_running_notification_text" msgid="1197581823314971177">"Gusa ili upate maelezo zaidi au usitishe programu."</string>
<string name="ok" msgid="5970060430562524910">"Sawa"</string>
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
<string name="yes" msgid="5362982303337969312">"Sawa"</string>
@@ -1062,7 +1062,7 @@
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Programu zinaanza"</string>
<string name="android_upgrading_complete" msgid="1405954754112999229">"Inamaliza kuwasha."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string>
- <string name="heavy_weight_notification_detail" msgid="867643381388543170">"Gonga ili uende kwenye programu"</string>
+ <string name="heavy_weight_notification_detail" msgid="867643381388543170">"Gusa ili uende kwenye programu"</string>
<string name="heavy_weight_switcher_title" msgid="7153167085403298169">"Badilisha programu?"</string>
<string name="heavy_weight_switcher_text" msgid="7022631924534406403">"Programmu nyingine tayari inaendeshwa na lazima hiyo ikomeshwe kabla ya kuanza nyingine mpya."</string>
<string name="old_app_action" msgid="493129172238566282">"Rejea katika <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
@@ -1107,7 +1107,7 @@
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Inaunganisha kwenye mtandao wa Wi‑Fi unaotumiwa na mtu yeyote"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Imeunganisha kwenye mtandao wa Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Imeshindwa kuunganisha kwenye mtandao wa Wi‑Fi"</string>
- <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gonga ili uone mitandao yote"</string>
+ <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gusa ili uone mitandao yote"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Unganisha"</string>
<string name="wifi_available_action_all_networks" msgid="1100098935861622985">"Mitandao Yote"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Ingia kwa mtandao wa Wi-Fi"</string>
@@ -1115,7 +1115,7 @@
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
<skip />
<string name="wifi_no_internet" msgid="8451173622563841546">"Wi-Fi haina muunganisho wa intaneti"</string>
- <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gonga ili upate chaguo"</string>
+ <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gusa ili upate chaguo"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="5325661434777870353">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina Intaneti. Huenda ukalipishwa."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1136,7 +1136,7 @@
<string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Anzisha Wi-Fi Moja kwa Moja. Hii itazima mteja/mtandao-hewa wa Wi-Fi."</string>
<string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Haikuweza kuanzisha Wi-Fi Moja kwa Moja."</string>
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ya Moja kwa Moja imewashwa"</string>
- <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Gonga ili uweke mipangilio"</string>
+ <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Gusa ili uweke mipangilio"</string>
<string name="accept" msgid="1645267259272829559">"Kubali"</string>
<string name="decline" msgid="2112225451706137894">"Kataa"</string>
<string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Mwaliko umetumwa"</string>
@@ -1172,7 +1172,7 @@
<string name="carrier_app_dialog_button" msgid="7900235513678617329">"PATA PROGRAMU"</string>
<string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"SIYO SASA"</string>
<string name="carrier_app_notification_title" msgid="8921767385872554621">"SIM mpya imewekwa"</string>
- <string name="carrier_app_notification_text" msgid="1132487343346050225">"Gonga ili uiweke"</string>
+ <string name="carrier_app_notification_text" msgid="1132487343346050225">"Gusa ili uiweke"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Weka saa"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Weka tarehe"</string>
<string name="date_time_set" msgid="5777075614321087758">"Weka"</string>
@@ -1188,11 +1188,11 @@
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB kwa ajili ya kuhamisha picha"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB kwa ajili ya MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Imeunganishwa kwa kifuasi cha USB"</string>
- <string name="usb_notification_message" msgid="3370903770828407960">"Gonga ili upate chaguo zaidi."</string>
+ <string name="usb_notification_message" msgid="3370903770828407960">"Gusa ili upate chaguo zaidi."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Imetambua kifaa cha sauti ya analogi"</string>
- <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Kifaa ulichoambatisha hakitumiki kwenye simu hii. Gonga ili upate maelezo zaidi."</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Kifaa ulichoambatisha hakitumiki kwenye simu hii. Gusa ili upate maelezo zaidi."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Utatuaji wa USB umeunganishwa"</string>
- <string name="adb_active_notification_message" msgid="4948470599328424059">"Gonga ili uzime utatuaji wa USB."</string>
+ <string name="adb_active_notification_message" msgid="4948470599328424059">"Gusa ili uzime utatuaji wa USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chagua ili kulemaza utatuaji USB."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Inatayarisha ripoti ya hitilafu…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Ungependa kushiriki ripoti ya hitilafu?"</string>
@@ -1204,23 +1204,23 @@
<string name="show_ime" msgid="2506087537466597099">"Ionyeshe kwenye skrini wakati kibodi halisi inatumika"</string>
<string name="hardware" msgid="194658061510127999">"Onyesha kibodi pepe"</string>
<string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Sanidi kibodi halisi"</string>
- <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Gonga ili uchague lugha na muundo"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Gusa ili uchague lugha na muundo"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"Onyesha juu ya programu zingine"</string>
<string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> inachomoza juu ya programu zingine"</string>
<string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> inachomoza juu ya programu zingine."</string>
- <string name="alert_windows_notification_message" msgid="8917232109522912560">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> kutumia kipengele hiki, gonga ili ufungue mipangilio na ukizime."</string>
+ <string name="alert_windows_notification_message" msgid="8917232109522912560">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> kutumia kipengele hiki, gusa ili ufungue mipangilio na ukizime."</string>
<string name="alert_windows_notification_turn_off_action" msgid="3367294525884949878">"ZIMA"</string>
<string name="ext_media_checking_notification_title" msgid="5734005953288045806">"Inaandaa <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_checking_notification_message" msgid="4747432538578886744">"Inakagua hitilafu"</string>
<string name="ext_media_new_notification_message" msgid="7589986898808506239">"<xliff:g id="NAME">%s</xliff:g> mpya imegunduliwa"</string>
<string name="ext_media_ready_notification_message" msgid="4083398150380114462">"Kwa ajili ya kuhamisha picha na maudhui"</string>
<string name="ext_media_unmountable_notification_title" msgid="8295123366236989588">"<xliff:g id="NAME">%s</xliff:g> iliyoharibika"</string>
- <string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> ina hitilafu. Gonga ili uirekebishe."</string>
+ <string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> ina hitilafu. Gusa ili uirekebishe."</string>
<string name="ext_media_unmountable_notification_message" product="tv" msgid="3941179940297874950">"<xliff:g id="NAME">%s</xliff:g> imeharibika. Ichague ili uirekebishe."</string>
<string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"<xliff:g id="NAME">%s</xliff:g> isiyotumika"</string>
- <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g>. Gonga ili uweke mipangilio ya muundo unaoweza kutumika."</string>
+ <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g>. Gusa ili uweke mipangilio ya muundo unaoweza kutumika."</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g> hii. Ichague ili uweke muundo unaotumika."</string>
<string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> imeondolewa bila kutarajiwa"</string>
<string name="ext_media_badremoval_notification_message" msgid="380176703346946313">"Ondoa <xliff:g id="NAME">%s</xliff:g> kabla ya kuchomoa ili kuepuka kupoteza data"</string>
@@ -1261,7 +1261,7 @@
<string name="permdesc_requestDeletePackages" msgid="3406172963097595270">"Huruhusu programu kuomba idhini ya kufuta vifurushi."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"omba kupuuza uimarishji wa betri"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Huruhusu programu kuomba ruhusa ya kupuuza uimarishaji wa betri katika programu yako."</string>
- <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Gonga mara mbili kwa udhibiti wa kuza"</string>
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Gusa mara mbili kwa udhibiti wa kuza"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Haikuweza kuongeza wijeti."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Nenda"</string>
<string name="ime_action_search" msgid="658110271822807811">"Tafuta"</string>
@@ -1292,8 +1292,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Huduma ya kupanga arifa"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN imewezeshwa"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN imeamilishwa na <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="vpn_text" msgid="1610714069627824309">"Gonga ili kudhibiti mtandao."</string>
- <string name="vpn_text_long" msgid="4907843483284977618">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gonga ili kudhibiti mtandao"</string>
+ <string name="vpn_text" msgid="1610714069627824309">"Gusa ili kudhibiti mtandao."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gusa ili kudhibiti mtandao"</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kila mara VPN iliyowashwa inaunganishwa…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Kila mara VPN iliyowashwa imeunganishwa"</string>
<string name="vpn_lockdown_disconnected" msgid="735805531187559719">"Imeondolewa kwenye VPN iliyowashwa kila wakati"</string>
@@ -1304,9 +1304,9 @@
<string name="reset" msgid="2448168080964209908">"Weka upya"</string>
<string name="submit" msgid="1602335572089911941">"Wasilisha"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mtindo wa gari umewezeshwa"</string>
- <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Gonga ili ufunge hali ya garini."</string>
+ <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Gusa ili ufunge hali ya garini."</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string>
- <string name="tethered_notification_message" msgid="2113628520792055377">"Gonga ili uweke mipangilio."</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Gusa ili uweke mipangilio."</string>
<string name="disable_tether_notification_title" msgid="7526977944111313195">"Umezima kipengele cha kusambaza mtandao"</string>
<string name="disable_tether_notification_message" msgid="2913366428516852495">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
<string name="back_button_label" msgid="2300470004503343439">"Nyuma"</string>
@@ -1383,7 +1383,7 @@
<string name="storage_usb" msgid="3017954059538517278">"Hifadhi ya USB"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Badilisha"</string>
<string name="data_usage_warning_title" msgid="3620440638180218181">"Tahadhari ya matumizi ya data"</string>
- <string name="data_usage_warning_body" msgid="6660692274311972007">"Gonga ili uangalie matumizi na mipangilio."</string>
+ <string name="data_usage_warning_body" msgid="6660692274311972007">"Gusa ili uangalie matumizi na mipangilio."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Kikomo data ya 2G-3G kimefikiwa"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Kikomo cha data ya 4G kimefikiwa"</string>
<string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"Umefikisha kipimo cha juu cha data"</string>
@@ -1395,7 +1395,7 @@
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Taarifa za Wi-fi zimevuka kiwanga"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> juu ya kikomo kilichobainishwa."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Data ya mandhari nyuma imezuiwa"</string>
- <string name="data_usage_restricted_body" msgid="469866376337242726">"Gonga ili uondoe kizuizi."</string>
+ <string name="data_usage_restricted_body" msgid="469866376337242726">"Gusa ili uondoe kizuizi."</string>
<string name="ssl_certificate" msgid="6510040486049237639">"Cheti cha usalama"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"Cheti hiki ni halali."</string>
<string name="issued_to" msgid="454239480274921032">"Kimetolewa kwa:"</string>
@@ -1594,7 +1594,7 @@
<string name="reason_unknown" msgid="6048913880184628119">"haijulikani"</string>
<string name="reason_service_unavailable" msgid="7824008732243903268">"Huduma ya uchapishaji haijawashwa"</string>
<string name="print_service_installed_title" msgid="2246317169444081628">"Huduma ya <xliff:g id="NAME">%s</xliff:g> imesakinisha"</string>
- <string name="print_service_installed_message" msgid="5897362931070459152">"Gonga ili uwashe"</string>
+ <string name="print_service_installed_message" msgid="5897362931070459152">"Gusa ili uwashe"</string>
<string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Weka PIN ya msimamizi"</string>
<string name="restr_pin_enter_pin" msgid="3395953421368476103">"Ingiza PIN"</string>
<string name="restr_pin_incorrect" msgid="8571512003955077924">"Sio sahihi"</string>
@@ -1722,12 +1722,12 @@
<string name="new_sms_notification_title" msgid="8442817549127555977">"Una ujumbe mpya"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Fungua programu ya SMS ili uweze kuangalia"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Huenda baadhi ya utendaji ukawa vikwazo"</string>
- <string name="user_encrypted_message" msgid="4923292604515744267">"Gonga ili ufungue"</string>
+ <string name="user_encrypted_message" msgid="4923292604515744267">"Gusa ili ufungue"</string>
<string name="user_encrypted_detail" msgid="5708447464349420392">"Data ya mtumiaji imefungwa"</string>
<string name="profile_encrypted_detail" msgid="3700965619978314974">"Wasifu wa kazini umefungwa"</string>
- <string name="profile_encrypted_message" msgid="6964994232310195874">"Gonga ili ufungue wasifu wa kazini"</string>
+ <string name="profile_encrypted_message" msgid="6964994232310195874">"Gusa ili ufungue wasifu wa kazini"</string>
<string name="usb_mtp_launch_notification_title" msgid="8359219638312208932">"Imeunganishwa na <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
- <string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"Gonga ili uangalie faili"</string>
+ <string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"Gusa ili uangalie faili"</string>
<string name="pin_target" msgid="3052256031352291362">"Bandika"</string>
<string name="unpin_target" msgid="3556545602439143442">"Bandua"</string>
<string name="app_info" msgid="6856026610594615344">"Maelezo ya programu"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9e0722bc0de0..7a3fa1a7b512 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -152,7 +152,7 @@
<!-- Displayed to tell the user that they should switch their network preference. -->
<string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
<!-- Displayed to tell the user that they should switch their network preference. -->
- <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings &gt; Network &amp; Internet &gt; Mobile networks &gt; Preferred network type."</string>
+ <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings &gt; Network &amp; internet &gt; Mobile networks &gt; Preferred network type."</string>
<!-- Displayed to tell the user that emergency calls might not be available. -->
<string name="EmergencyCallWarningTitle">Wi\u2011Fi calling is active</string>
<!-- Displayed to tell the user that emergency calls might not be available. -->
@@ -3014,7 +3014,7 @@
<string name="network_available_sign_in_detailed"><xliff:g id="network_ssid">%1$s</xliff:g></string>
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's title. -->
- <string name="wifi_no_internet">Wi-Fi has no Internet access</string>
+ <string name="wifi_no_internet">Wi-Fi has no internet access</string>
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
@@ -3023,7 +3023,7 @@
<string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
<!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's message. %1$s is the network that the device switched to, e.g., cellular data. %2$s is the network type the device switched from, e.g., Wi-Fi. Both are strings in the network_switch_type_name array. -->
- <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no Internet access. Charges may apply.</string>
+ <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no internet access. Charges may apply.</string>
<!-- A toast might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the text of the toast. %1$s is the network that the device switched from, e.g., Wi-Fi. %2$s is the network type the device switched from, e.g., cellular data. Both are strings in the network_switch_type_name array. -->
<string name="network_switch_metered_toast">Switched from <xliff:g id="previous_network">%1$s</xliff:g> to <xliff:g id="new_network">%2$s</xliff:g></string>
@@ -3043,7 +3043,7 @@
<!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. This is the notification's title / ticker. -->
<string name="wifi_watchdog_network_disabled">Couldn\'t connect to Wi-Fi</string>
<!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. The complete alert msg is: <hotspot name> + this string, i.e. "Linksys has a poor internet connection" -->
- <string name="wifi_watchdog_network_disabled_detailed">\u0020has a poor Internet connection.</string>
+ <string name="wifi_watchdog_network_disabled_detailed">\u0020has a poor internet connection.</string>
<!-- Do not translate. Default access point SSID used for tethering -->
<string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index d36ed639b4b9..0d35f4e1b443 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -356,6 +356,9 @@ public class SettingsBackupTest {
Settings.Global.USE_GOOGLE_MAIL,
Settings.Global.VT_IMS_ENABLED,
Settings.Global.WAIT_FOR_DEBUGGER,
+ Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
+ Settings.Global.GPU_DEBUG_APP,
+ Settings.Global.GPU_DEBUG_LAYERS,
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 24e3646e02fc..dee51dcbc7ff 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -117,13 +117,13 @@ public class FormatterTest {
assertEquals("2 days", Formatter.formatShortElapsedTime(mContext, 2 * DAY));
assertEquals("1 day, 23 hr",
Formatter.formatShortElapsedTime(mContext, 1 * DAY + 23 * HOUR + 59 * MINUTE));
- assertEquals("1 day, 0 hr",
+ assertEquals("1 day",
Formatter.formatShortElapsedTime(mContext, 1 * DAY + 59 * MINUTE));
- assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTime(mContext, 1 * DAY));
+ assertEquals("1 day", Formatter.formatShortElapsedTime(mContext, 1 * DAY));
assertEquals("24 hr", Formatter.formatShortElapsedTime(mContext, 23 * HOUR + 30 * MINUTE));
assertEquals("3 hr", Formatter.formatShortElapsedTime(mContext, 2 * HOUR + 30 * MINUTE));
assertEquals("2 hr", Formatter.formatShortElapsedTime(mContext, 2 * HOUR));
- assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTime(mContext, 1 * HOUR));
+ assertEquals("1 hr", Formatter.formatShortElapsedTime(mContext, 1 * HOUR));
assertEquals("60 min",
Formatter.formatShortElapsedTime(mContext, 59 * MINUTE + 30 * SECOND));
assertEquals("59 min",
@@ -132,7 +132,7 @@ public class FormatterTest {
assertEquals("2 min", Formatter.formatShortElapsedTime(mContext, 2 * MINUTE));
assertEquals("1 min, 59 sec",
Formatter.formatShortElapsedTime(mContext, 1 * MINUTE + 59 * SECOND + 999));
- assertEquals("1 min, 0 sec", Formatter.formatShortElapsedTime(mContext, 1 * MINUTE));
+ assertEquals("1 min", Formatter.formatShortElapsedTime(mContext, 1 * MINUTE));
assertEquals("59 sec", Formatter.formatShortElapsedTime(mContext, 59 * SECOND + 999));
assertEquals("1 sec", Formatter.formatShortElapsedTime(mContext, 1 * SECOND));
assertEquals("0 sec", Formatter.formatShortElapsedTime(mContext, 1));
@@ -154,9 +154,9 @@ public class FormatterTest {
mContext, 2 * DAY));
assertEquals("1 day, 23 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * DAY + 23 * HOUR + 59 * MINUTE));
- assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 day", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * DAY + 59 * MINUTE));
- assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 day", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * DAY));
assertEquals("24 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 23 * HOUR + 30 * MINUTE));
@@ -164,9 +164,9 @@ public class FormatterTest {
mContext, 2 * HOUR + 30 * MINUTE));
assertEquals("2 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 2 * HOUR));
- assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * HOUR));
- assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 59 * MINUTE + 30 * SECOND));
assertEquals("59 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 59 * MINUTE));
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index c5d175b4245f..b8c41233f6f5 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -22,6 +22,7 @@ import static junit.framework.Assert.assertTrue;
import android.content.Context;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -51,6 +52,7 @@ public class FeatureFlagUtilsTest {
}
private void cleanup() {
+ Settings.Global.putString(mContext.getContentResolver(), TEST_FEATURE_NAME, "");
SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "");
SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "");
}
@@ -63,7 +65,7 @@ public class FeatureFlagUtilsTest {
}
@Test
- public void testGetFlag_override_shouldReturnTrue() {
+ public void testGetFlag_adb_override_shouldReturnTrue() {
SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "false");
SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "true");
@@ -71,10 +73,20 @@ public class FeatureFlagUtilsTest {
}
@Test
+ public void testGetFlag_settings_override_shouldReturnTrue() {
+ SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "false");
+ SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "false");
+
+ Settings.Global.putString(mContext.getContentResolver(), TEST_FEATURE_NAME, "true");
+
+ assertTrue(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
+ }
+
+ @Test
public void testSetEnabled_shouldSetOverrideFlag() {
assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
- FeatureFlagUtils.setEnabled(TEST_FEATURE_NAME, true);
+ FeatureFlagUtils.setEnabled(null /* context */, TEST_FEATURE_NAME, true);
assertEquals(SystemProperties.get(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, null),
"");
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 7b2fa76f01df..76eb4e676923 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -112,14 +112,29 @@ include $(BUILD_PREBUILT)
# Run sanity tests on fonts on checkbuild
checkbuild: fontchain_lint
-FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py
+FONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter
ifeq ($(MINIMAL_FONT_FOOTPRINT),true)
CHECK_EMOJI := false
else
CHECK_EMOJI := true
endif
+fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp
+
.PHONY: fontchain_lint
-fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img
- PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \
- python $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+fontchain_lint: $(fontchain_lint_timestamp)
+
+fontchain_lint_deps := \
+ external/unicode/DerivedAge.txt \
+ external/unicode/emoji-data.txt \
+ external/unicode/emoji-sequences.txt \
+ external/unicode/emoji-variation-sequences.txt \
+ external/unicode/emoji-zwj-sequences.txt \
+ external/unicode/additions/emoji-data.txt \
+ external/unicode/additions/emoji-sequences.txt \
+ external/unicode/additions/emoji-zwj-sequences.txt \
+
+$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps)
+ @echo Running fontchain lint
+ $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+ touch $@
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 209f364e041b..dad24da6ad98 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -115,10 +115,14 @@
<family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
<font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="elegant">
<font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="compact">
<font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
@@ -127,14 +131,20 @@
<family lang="und-Armn">
<font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Bold.ttf</font>
</family>
<family lang="und-Geor und-Geok">
<font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-Bold.ttf</font>
</family>
<family lang="und-Deva" variant="elegant">
<font weight="400" style="normal">NotoSansDevanagari-Regular.ttf</font>
<font weight="700" style="normal">NotoSansDevanagari-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-Bold.ttf</font>
</family>
<family lang="und-Deva" variant="compact">
<font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
@@ -147,6 +157,8 @@
<family lang="und-Gujr" variant="elegant">
<font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-Bold.ttf</font>
</family>
<family lang="und-Gujr" variant="compact">
<font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
@@ -163,6 +175,8 @@
<family lang="und-Taml" variant="elegant">
<font weight="400" style="normal">NotoSansTamil-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTamil-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-Bold.ttf</font>
</family>
<family lang="und-Taml" variant="compact">
<font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
@@ -171,6 +185,8 @@
<family lang="und-Mlym" variant="elegant">
<font weight="400" style="normal">NotoSansMalayalam-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMalayalam-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-Bold.ttf</font>
</family>
<family lang="und-Mlym" variant="compact">
<font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
@@ -179,6 +195,8 @@
<family lang="und-Beng" variant="elegant">
<font weight="400" style="normal">NotoSansBengali-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengali-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-Bold.ttf</font>
</family>
<family lang="und-Beng" variant="compact">
<font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
@@ -187,6 +205,8 @@
<family lang="und-Telu" variant="elegant">
<font weight="400" style="normal">NotoSansTelugu-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTelugu-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-Bold.ttf</font>
</family>
<family lang="und-Telu" variant="compact">
<font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
@@ -195,6 +215,8 @@
<family lang="und-Knda" variant="elegant">
<font weight="400" style="normal">NotoSansKannada-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKannada-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-Bold.ttf</font>
</family>
<family lang="und-Knda" variant="compact">
<font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
@@ -258,6 +280,8 @@
<family lang="und-Laoo" variant="elegant">
<font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="compact">
<font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
@@ -472,15 +496,19 @@
</family>
<family lang="zh-Hans">
<font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="zh-Hant zh-Bopo">
<font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="ja">
<font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="ko">
<font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="und-Zsye">
<font weight="400" style="normal">NotoColorEmoji.ttf</font>
diff --git a/docs/html/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png b/docs/html/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
new file mode 100644
index 000000000000..7578b48770fb
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
new file mode 100644
index 000000000000..7b10f6b7c812
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
new file mode 100644
index 000000000000..41972cfd71b9
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
new file mode 100644
index 000000000000..d26600b37aca
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
new file mode 100644
index 000000000000..1e7208ed79a2
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
new file mode 100644
index 000000000000..ecef3ae7998e
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
new file mode 100644
index 000000000000..a02fd893b286
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
new file mode 100644
index 000000000000..c309ac5ea887
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
new file mode 100644
index 000000000000..414fad49a919
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
new file mode 100644
index 000000000000..c147a8729f17
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
new file mode 100644
index 000000000000..4ce2125f3c91
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
Binary files differ
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 3bf4f90257b7..dea194e4ffde 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -36,138 +36,69 @@ class RippleBackground extends RippleComponent {
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- private static final int OPACITY_ENTER_DURATION = 600;
- private static final int OPACITY_ENTER_DURATION_FAST = 120;
- private static final int OPACITY_EXIT_DURATION = 480;
+ private static final int OPACITY_DURATION = 80;
- // Hardware rendering properties.
- private CanvasProperty<Paint> mPropPaint;
- private CanvasProperty<Float> mPropRadius;
- private CanvasProperty<Float> mPropX;
- private CanvasProperty<Float> mPropY;
+ private ObjectAnimator mAnimator;
- // Software rendering properties.
private float mOpacity = 0;
/** Whether this ripple is bounded. */
private boolean mIsBounded;
- public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded,
- boolean forceSoftware) {
- super(owner, bounds, forceSoftware);
+ private boolean mFocused = false;
+ private boolean mHovered = false;
+
+ public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded) {
+ super(owner, bounds);
mIsBounded = isBounded;
}
public boolean isVisible() {
- return mOpacity > 0 || isHardwareAnimating();
+ return mOpacity > 0;
}
- @Override
- protected boolean drawSoftware(Canvas c, Paint p) {
- boolean hasContent = false;
-
+ public void draw(Canvas c, Paint p) {
final int origAlpha = p.getAlpha();
- final int alpha = (int) (origAlpha * mOpacity + 0.5f);
+ final int alpha = Math.min((int) (origAlpha * mOpacity + 0.5f), 255);
if (alpha > 0) {
p.setAlpha(alpha);
c.drawCircle(0, 0, mTargetRadius, p);
p.setAlpha(origAlpha);
- hasContent = true;
}
-
- return hasContent;
- }
-
- @Override
- protected boolean drawHardware(DisplayListCanvas c) {
- c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
- return true;
- }
-
- @Override
- protected Animator createSoftwareEnter(boolean fast) {
- // Linear enter based on current opacity.
- final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION;
- final int duration = (int) ((1 - mOpacity) * maxDuration);
-
- final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
- opacity.setAutoCancel(true);
- opacity.setDuration(duration);
- opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- return opacity;
}
- @Override
- protected Animator createSoftwareExit() {
- final AnimatorSet set = new AnimatorSet();
-
- // Linear exit after enter is completed.
- final ObjectAnimator exit = ObjectAnimator.ofFloat(this, OPACITY, 0);
- exit.setInterpolator(LINEAR_INTERPOLATOR);
- exit.setDuration(OPACITY_EXIT_DURATION);
- exit.setAutoCancel(true);
-
- final AnimatorSet.Builder builder = set.play(exit);
-
- // Linear "fast" enter based on current opacity.
- final int fastEnterDuration = mIsBounded ?
- (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
- if (fastEnterDuration > 0) {
- final ObjectAnimator enter = ObjectAnimator.ofFloat(this, OPACITY, 1);
- enter.setInterpolator(LINEAR_INTERPOLATOR);
- enter.setDuration(fastEnterDuration);
- enter.setAutoCancel(true);
-
- builder.after(enter);
+ public void setState(boolean focused, boolean hovered, boolean animateChanged) {
+ if (mHovered != hovered || mFocused != focused) {
+ mHovered = hovered;
+ mFocused = focused;
+ onStateChanged(animateChanged);
}
-
- return set;
}
- @Override
- protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
- final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
-
- final int targetAlpha = p.getAlpha();
- final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f);
- p.setAlpha(currentAlpha);
-
- mPropPaint = CanvasProperty.createPaint(p);
- mPropRadius = CanvasProperty.createFloat(mTargetRadius);
- mPropX = CanvasProperty.createFloat(0);
- mPropY = CanvasProperty.createFloat(0);
-
- final int fastEnterDuration = mIsBounded ?
- (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
-
- // Linear exit after enter is completed.
- final RenderNodeAnimator exit = new RenderNodeAnimator(
- mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
- exit.setInterpolator(LINEAR_INTERPOLATOR);
- exit.setDuration(OPACITY_EXIT_DURATION);
- if (fastEnterDuration > 0) {
- exit.setStartDelay(fastEnterDuration);
- exit.setStartValue(targetAlpha);
+ private void onStateChanged(boolean animateChanged) {
+ float newOpacity = 0.0f;
+ if (mHovered) newOpacity += 1.0f;
+ if (mFocused) newOpacity += 1.0f;
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ mAnimator = null;
}
- set.add(exit);
-
- // Linear "fast" enter based on current opacity.
- if (fastEnterDuration > 0) {
- final RenderNodeAnimator enter = new RenderNodeAnimator(
- mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha);
- enter.setInterpolator(LINEAR_INTERPOLATOR);
- enter.setDuration(fastEnterDuration);
- set.add(enter);
+ if (animateChanged) {
+ mAnimator = ObjectAnimator.ofFloat(this, OPACITY, newOpacity);
+ mAnimator.setDuration(OPACITY_DURATION);
+ mAnimator.setInterpolator(LINEAR_INTERPOLATOR);
+ mAnimator.start();
+ } else {
+ mOpacity = newOpacity;
}
-
- return set;
}
- @Override
- protected void jumpValuesToExit() {
- mOpacity = 0;
+ public void jumpToFinal() {
+ if (mAnimator != null) {
+ mAnimator.end();
+ mAnimator = null;
+ }
}
private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> {
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index e83513c644db..0e38826eb34b 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -27,23 +27,14 @@ import android.view.RenderNodeAnimator;
import java.util.ArrayList;
/**
- * Abstract class that handles hardware/software hand-off and lifecycle for
- * animated ripple foreground and background components.
+ * Abstract class that handles size & positioning common to the ripple & focus states.
*/
abstract class RippleComponent {
- private final RippleDrawable mOwner;
+ protected final RippleDrawable mOwner;
/** Bounds used for computing max radius. May be modified by the owner. */
protected final Rect mBounds;
- /** Whether we can use hardware acceleration for the exit animation. */
- private boolean mHasDisplayListCanvas;
-
- private boolean mHasPendingHardwareAnimator;
- private RenderNodeAnimatorSet mHardwareAnimator;
-
- private Animator mSoftwareAnimator;
-
/** Whether we have an explicit maximum radius. */
private boolean mHasMaxRadius;
@@ -53,16 +44,9 @@ abstract class RippleComponent {
/** Screen density used to adjust pixel-based constants. */
protected float mDensityScale;
- /**
- * If set, force all ripple animations to not run on RenderThread, even if it would be
- * available.
- */
- private final boolean mForceSoftware;
-
- public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
+ public RippleComponent(RippleDrawable owner, Rect bounds) {
mOwner = owner;
mBounds = bounds;
- mForceSoftware = forceSoftware;
}
public void onBoundsChange() {
@@ -92,89 +76,6 @@ abstract class RippleComponent {
}
/**
- * Starts a ripple enter animation.
- *
- * @param fast whether the ripple should enter quickly
- */
- public final void enter(boolean fast) {
- cancel();
-
- mSoftwareAnimator = createSoftwareEnter(fast);
-
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.start();
- }
- }
-
- /**
- * Starts a ripple exit animation.
- */
- public final void exit() {
- cancel();
-
- if (mHasDisplayListCanvas) {
- // We don't have access to a canvas here, but we expect one on the
- // next frame. We'll start the render thread animation then.
- mHasPendingHardwareAnimator = true;
-
- // Request another frame.
- invalidateSelf();
- } else {
- mSoftwareAnimator = createSoftwareExit();
- mSoftwareAnimator.start();
- }
- }
-
- /**
- * Cancels all animations. Software animation values are left in the
- * current state, while hardware animation values jump to the end state.
- */
- public void cancel() {
- cancelSoftwareAnimations();
- endHardwareAnimations();
- }
-
- /**
- * Ends all animations, jumping values to the end state.
- */
- public void end() {
- endSoftwareAnimations();
- endHardwareAnimations();
- }
-
- /**
- * Draws the ripple to the canvas, inheriting the paint's color and alpha
- * properties.
- *
- * @param c the canvas to which the ripple should be drawn
- * @param p the paint used to draw the ripple
- * @return {@code true} if something was drawn, {@code false} otherwise
- */
- public boolean draw(Canvas c, Paint p) {
- final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated()
- && c instanceof DisplayListCanvas;
- if (mHasDisplayListCanvas != hasDisplayListCanvas) {
- mHasDisplayListCanvas = hasDisplayListCanvas;
-
- if (!hasDisplayListCanvas) {
- // We've switched from hardware to non-hardware mode. Panic.
- endHardwareAnimations();
- }
- }
-
- if (hasDisplayListCanvas) {
- final DisplayListCanvas hw = (DisplayListCanvas) c;
- startPendingAnimation(hw, p);
-
- if (mHardwareAnimator != null) {
- return drawHardware(hw);
- }
- }
-
- return drawSoftware(c, p);
- }
-
- /**
* Populates {@code bounds} with the maximum drawing bounds of the ripple
* relative to its center. The resulting bounds should be translated into
* parent drawable coordinates before use.
@@ -186,77 +87,10 @@ abstract class RippleComponent {
bounds.set(-r, -r, r, r);
}
- /**
- * Starts the pending hardware animation, if available.
- *
- * @param hw hardware canvas on which the animation should draw
- * @param p paint whose properties the hardware canvas should use
- */
- private void startPendingAnimation(DisplayListCanvas hw, Paint p) {
- if (mHasPendingHardwareAnimator) {
- mHasPendingHardwareAnimator = false;
-
- mHardwareAnimator = createHardwareExit(new Paint(p));
- mHardwareAnimator.start(hw);
-
- // Preemptively jump the software values to the end state now that
- // the hardware exit has read whatever values it needs.
- jumpValuesToExit();
- }
- }
-
- /**
- * Cancels any current software animations, leaving the values in their
- * current state.
- */
- private void cancelSoftwareAnimations() {
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.cancel();
- mSoftwareAnimator = null;
- }
- }
-
- /**
- * Ends any current software animations, jumping the values to their end
- * state.
- */
- private void endSoftwareAnimations() {
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.end();
- mSoftwareAnimator = null;
- }
- }
-
- /**
- * Ends any pending or current hardware animations.
- * <p>
- * Hardware animations can't synchronize values back to the software
- * thread, so there is no "cancel" equivalent.
- */
- private void endHardwareAnimations() {
- if (mHardwareAnimator != null) {
- mHardwareAnimator.end();
- mHardwareAnimator = null;
- }
-
- if (mHasPendingHardwareAnimator) {
- mHasPendingHardwareAnimator = false;
-
- // Manually jump values to their exited state. Normally we'd do that
- // later when starting the hardware exit, but we're aborting early.
- jumpValuesToExit();
- }
- }
-
protected final void invalidateSelf() {
mOwner.invalidateSelf(false);
}
- protected final boolean isHardwareAnimating() {
- return mHardwareAnimator != null && mHardwareAnimator.isRunning()
- || mHasPendingHardwareAnimator;
- }
-
protected final void onHotspotBoundsChanged() {
if (!mHasMaxRadius) {
final float halfWidth = mBounds.width() / 2.0f;
@@ -276,76 +110,4 @@ abstract class RippleComponent {
protected void onTargetRadiusChanged(float targetRadius) {
// Stub.
}
-
- protected abstract Animator createSoftwareEnter(boolean fast);
-
- protected abstract Animator createSoftwareExit();
-
- protected abstract RenderNodeAnimatorSet createHardwareExit(Paint p);
-
- protected abstract boolean drawHardware(DisplayListCanvas c);
-
- protected abstract boolean drawSoftware(Canvas c, Paint p);
-
- /**
- * Called when the hardware exit is cancelled. Jumps software values to end
- * state to ensure that software and hardware values are synchronized.
- */
- protected abstract void jumpValuesToExit();
-
- public static class RenderNodeAnimatorSet {
- private final ArrayList<RenderNodeAnimator> mAnimators = new ArrayList<>();
-
- public void add(RenderNodeAnimator anim) {
- mAnimators.add(anim);
- }
-
- public void clear() {
- mAnimators.clear();
- }
-
- public void start(DisplayListCanvas target) {
- if (target == null) {
- throw new IllegalArgumentException("Hardware canvas must be non-null");
- }
-
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.setTarget(target);
- anim.start();
- }
- }
-
- public void cancel() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.cancel();
- }
- }
-
- public void end() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.end();
- }
- }
-
- public boolean isRunning() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- if (anim.isRunning()) {
- return true;
- }
- }
- return false;
- }
- }
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 1727eca544a9..8b185f2b8903 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -16,11 +16,6 @@
package android.graphics.drawable;
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo.Config;
@@ -42,6 +37,11 @@ import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.util.Arrays;
@@ -135,9 +135,6 @@ public class RippleDrawable extends LayerDrawable {
private PorterDuffColorFilter mMaskColorFilter;
private boolean mHasValidMask;
- /** Whether we expect to draw a background when visible. */
- private boolean mBackgroundActive;
-
/** The current ripple. May be actively animating or pending entry. */
private RippleForeground mRipple;
@@ -217,7 +214,7 @@ public class RippleDrawable extends LayerDrawable {
}
if (mBackground != null) {
- mBackground.end();
+ mBackground.jumpToFinal();
}
cancelExitingRipples();
@@ -266,9 +263,9 @@ public class RippleDrawable extends LayerDrawable {
}
}
- setRippleActive(focused || (enabled && pressed));
+ setRippleActive(enabled && pressed);
- setBackgroundActive(hovered, hovered);
+ setBackgroundActive(hovered, focused);
return changed;
}
@@ -283,14 +280,13 @@ public class RippleDrawable extends LayerDrawable {
}
}
- private void setBackgroundActive(boolean active, boolean focused) {
- if (mBackgroundActive != active) {
- mBackgroundActive = active;
- if (active) {
- tryBackgroundEnter(focused);
- } else {
- tryBackgroundExit();
- }
+ private void setBackgroundActive(boolean hovered, boolean focused) {
+ if (mBackground == null && (hovered || focused)) {
+ mBackground = new RippleBackground(this, mHotspotBounds, isBounded());
+ mBackground.setup(mState.mMaxRadius, mDensity);
+ }
+ if (mBackground != null) {
+ mBackground.setState(focused, hovered, true);
}
}
@@ -327,10 +323,6 @@ public class RippleDrawable extends LayerDrawable {
tryRippleEnter();
}
- if (mBackgroundActive) {
- tryBackgroundEnter(false);
- }
-
// Skip animations, just show the correct final states.
jumpToCurrentState();
}
@@ -546,26 +538,6 @@ public class RippleDrawable extends LayerDrawable {
}
/**
- * Creates an active hotspot at the specified location.
- */
- private void tryBackgroundEnter(boolean focused) {
- if (mBackground == null) {
- final boolean isBounded = isBounded();
- mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware);
- }
-
- mBackground.setup(mState.mMaxRadius, mDensity);
- mBackground.enter(focused);
- }
-
- private void tryBackgroundExit() {
- if (mBackground != null) {
- // Don't null out the background, we need it to draw!
- mBackground.exit();
- }
- }
-
- /**
* Attempts to start an enter animation for the active hotspot. Fails if
* there are too many animating ripples.
*/
@@ -593,7 +565,7 @@ public class RippleDrawable extends LayerDrawable {
}
mRipple.setup(mState.mMaxRadius, mDensity);
- mRipple.enter(false);
+ mRipple.enter();
}
/**
@@ -623,9 +595,7 @@ public class RippleDrawable extends LayerDrawable {
}
if (mBackground != null) {
- mBackground.end();
- mBackground = null;
- mBackgroundActive = false;
+ mBackground.setState(false, false, false);
}
cancelExitingRipples();
@@ -858,6 +828,40 @@ public class RippleDrawable extends LayerDrawable {
final float y = mHotspotBounds.exactCenterY();
canvas.translate(x, y);
+ final Paint p = getRipplePaint();
+
+ if (background != null && background.isVisible()) {
+ background.draw(canvas, p);
+ }
+
+ if (count > 0) {
+ final RippleForeground[] ripples = mExitingRipples;
+ for (int i = 0; i < count; i++) {
+ ripples[i].draw(canvas, p);
+ }
+ }
+
+ if (active != null) {
+ active.draw(canvas, p);
+ }
+
+ canvas.translate(-x, -y);
+ }
+
+ private void drawMask(Canvas canvas) {
+ mMask.draw(canvas);
+ }
+
+ Paint getRipplePaint() {
+ if (mRipplePaint == null) {
+ mRipplePaint = new Paint();
+ mRipplePaint.setAntiAlias(true);
+ mRipplePaint.setStyle(Paint.Style.FILL);
+ }
+
+ final float x = mHotspotBounds.exactCenterX();
+ final float y = mHotspotBounds.exactCenterY();
+
updateMaskShaderIfNeeded();
// Position the shader to account for canvas translation.
@@ -871,7 +875,7 @@ public class RippleDrawable extends LayerDrawable {
// half so that the ripple and background together yield full alpha.
final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
final int halfAlpha = (Color.alpha(color) / 2) << 24;
- final Paint p = getRipplePaint();
+ final Paint p = mRipplePaint;
if (mMaskColorFilter != null) {
// The ripple timing depends on the paint's alpha value, so we need
@@ -890,35 +894,7 @@ public class RippleDrawable extends LayerDrawable {
p.setShader(null);
}
- if (background != null && background.isVisible()) {
- background.draw(canvas, p);
- }
-
- if (count > 0) {
- final RippleForeground[] ripples = mExitingRipples;
- for (int i = 0; i < count; i++) {
- ripples[i].draw(canvas, p);
- }
- }
-
- if (active != null) {
- active.draw(canvas, p);
- }
-
- canvas.translate(-x, -y);
- }
-
- private void drawMask(Canvas canvas) {
- mMask.draw(canvas);
- }
-
- private Paint getRipplePaint() {
- if (mRipplePaint == null) {
- mRipplePaint = new Paint();
- mRipplePaint.setAntiAlias(true);
- mRipplePaint.setStyle(Paint.Style.FILL);
- }
- return mRipplePaint;
+ return p;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index a675eaf85297..0b5020cbe55c 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -18,7 +18,6 @@ package android.graphics.drawable;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
@@ -29,8 +28,11 @@ import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
+import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
+import java.util.ArrayList;
+
/**
* Draws a ripple foreground.
*/
@@ -40,7 +42,7 @@ class RippleForeground extends RippleComponent {
400f, 1.4f, 0);
// Pixel-based accelerations and velocities.
- private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024;
+ private static final float WAVE_TOUCH_DOWN_ACCELERATION = 2048;
private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
// Bounded ripple animation properties.
@@ -49,8 +51,9 @@ class RippleForeground extends RippleComponent {
private static final int BOUNDED_OPACITY_EXIT_DURATION = 400;
private static final float MAX_BOUNDED_RADIUS = 350;
- private static final int RIPPLE_ENTER_DELAY = 80;
- private static final int OPACITY_ENTER_DURATION_FAST = 120;
+ private static final int OPACITY_ENTER_DURATION = 75;
+ private static final int OPACITY_EXIT_DURATION = 150;
+ private static final int OPACITY_HOLD_DURATION = OPACITY_ENTER_DURATION + 150;
// Parent-relative values for starting position.
private float mStartingX;
@@ -72,7 +75,7 @@ class RippleForeground extends RippleComponent {
private float mBoundedRadius = 0;
// Software rendering properties.
- private float mOpacity = 1;
+ private float mOpacity = 0;
// Values used to tween between the start and end positions.
private float mTweenRadius = 0;
@@ -82,6 +85,22 @@ class RippleForeground extends RippleComponent {
/** Whether this ripple has finished its exit animation. */
private boolean mHasFinishedExit;
+ /** Whether we can use hardware acceleration for the exit animation. */
+ private boolean mUsingProperties;
+
+ private long mEnterStartedAtMillis;
+
+ private ArrayList<RenderNodeAnimator> mPendingHwAnimators = new ArrayList<>();
+ private ArrayList<RenderNodeAnimator> mRunningHwAnimators = new ArrayList<>();
+
+ private ArrayList<Animator> mRunningSwAnimators = new ArrayList<>();
+
+ /**
+ * If set, force all ripple animations to not run on RenderThread, even if it would be
+ * available.
+ */
+ private final boolean mForceSoftware;
+
/**
* If we have a bound, don't start from 0. Start from 60% of the max out of width and height.
*/
@@ -89,8 +108,9 @@ class RippleForeground extends RippleComponent {
public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
boolean isBounded, boolean forceSoftware) {
- super(owner, bounds, forceSoftware);
+ super(owner, bounds);
+ mForceSoftware = forceSoftware;
mStartingX = startingX;
mStartingY = startingY;
@@ -109,10 +129,7 @@ class RippleForeground extends RippleComponent {
clampStartingPosition();
}
- @Override
- protected boolean drawSoftware(Canvas c, Paint p) {
- boolean hasContent = false;
-
+ private void drawSoftware(Canvas c, Paint p) {
final int origAlpha = p.getAlpha();
final int alpha = (int) (origAlpha * mOpacity + 0.5f);
final float radius = getCurrentRadius();
@@ -122,16 +139,51 @@ class RippleForeground extends RippleComponent {
p.setAlpha(alpha);
c.drawCircle(x, y, radius, p);
p.setAlpha(origAlpha);
- hasContent = true;
}
+ }
- return hasContent;
+ private void startPending(DisplayListCanvas c) {
+ if (!mPendingHwAnimators.isEmpty()) {
+ for (int i = 0; i < mPendingHwAnimators.size(); i++) {
+ RenderNodeAnimator animator = mPendingHwAnimators.get(i);
+ animator.setTarget(c);
+ animator.start();
+ mRunningHwAnimators.add(animator);
+ }
+ mPendingHwAnimators.clear();
+ }
}
- @Override
- protected boolean drawHardware(DisplayListCanvas c) {
- c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
- return true;
+ private void pruneHwFinished() {
+ if (!mRunningHwAnimators.isEmpty()) {
+ for (int i = mRunningHwAnimators.size() - 1; i >= 0; i--) {
+ if (!mRunningHwAnimators.get(i).isRunning()) {
+ mRunningHwAnimators.remove(i);
+ }
+ }
+ }
+ }
+
+ private void pruneSwFinished() {
+ if (!mRunningSwAnimators.isEmpty()) {
+ for (int i = mRunningSwAnimators.size() - 1; i >= 0; i--) {
+ if (!mRunningSwAnimators.get(i).isRunning()) {
+ mRunningSwAnimators.remove(i);
+ }
+ }
+ }
+ }
+
+ private void drawHardware(DisplayListCanvas c, Paint p) {
+ startPending(c);
+ pruneHwFinished();
+ if (mPropPaint != null) {
+ mUsingProperties = true;
+ c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
+ } else {
+ mUsingProperties = false;
+ drawSoftware(c, p);
+ }
}
/**
@@ -162,141 +214,152 @@ class RippleForeground extends RippleComponent {
return mHasFinishedExit;
}
- @Override
- protected Animator createSoftwareEnter(boolean fast) {
+ private long computeFadeOutDelay() {
+ long timeSinceEnter = AnimationUtils.currentAnimationTimeMillis() - mEnterStartedAtMillis;
+ if (timeSinceEnter > 0 && timeSinceEnter < OPACITY_HOLD_DURATION) {
+ return OPACITY_HOLD_DURATION - timeSinceEnter;
+ }
+ return 0;
+ }
+
+ private void startSoftwareEnter() {
+ for (int i = 0; i < mRunningSwAnimators.size(); i++) {
+ mRunningSwAnimators.get(i).cancel();
+ }
+ mRunningSwAnimators.clear();
+
final int duration = getRadiusDuration();
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
- tweenRadius.setAutoCancel(true);
tweenRadius.setDuration(duration);
tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
- tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
+ tweenRadius.start();
+ mRunningSwAnimators.add(tweenRadius);
final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
- tweenOrigin.setAutoCancel(true);
tweenOrigin.setDuration(duration);
tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
- tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
+ tweenOrigin.start();
+ mRunningSwAnimators.add(tweenOrigin);
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
- opacity.setAutoCancel(true);
- opacity.setDuration(OPACITY_ENTER_DURATION_FAST);
+ opacity.setDuration(OPACITY_ENTER_DURATION);
opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- final AnimatorSet set = new AnimatorSet();
- set.play(tweenOrigin).with(tweenRadius).with(opacity);
-
- return set;
+ opacity.start();
+ mRunningSwAnimators.add(opacity);
}
- private float getCurrentX() {
- return MathUtils.lerp(mClampedStartingX - mBounds.exactCenterX(), mTargetX, mTweenX);
- }
-
- private float getCurrentY() {
- return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
- }
-
- private int getRadiusDuration() {
- final float remainingRadius = mTargetRadius - getCurrentRadius();
- return (int) (1000 * Math.sqrt(remainingRadius / WAVE_TOUCH_DOWN_ACCELERATION *
- mDensityScale) + 0.5);
- }
-
- private float getCurrentRadius() {
- return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius);
- }
-
- private int getOpacityExitDuration() {
- return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
- }
-
- @Override
- protected Animator createSoftwareExit() {
- final int radiusDuration;
- final int originDuration;
- final int opacityDuration;
-
- radiusDuration = getRadiusDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
-
- final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
- tweenRadius.setAutoCancel(true);
- tweenRadius.setDuration(radiusDuration);
- tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
-
- final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
- tweenOrigin.setAutoCancel(true);
- tweenOrigin.setDuration(originDuration);
- tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
-
+ private void startSoftwareExit() {
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0);
- opacity.setAutoCancel(true);
- opacity.setDuration(opacityDuration);
+ opacity.setDuration(OPACITY_EXIT_DURATION);
opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- final AnimatorSet set = new AnimatorSet();
- set.play(tweenOrigin).with(tweenRadius).with(opacity);
- set.addListener(mAnimationListener);
-
- return set;
+ opacity.addListener(mAnimationListener);
+ opacity.setStartDelay(computeFadeOutDelay());
+ opacity.start();
+ mRunningSwAnimators.add(opacity);
}
- @Override
- protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
- final int radiusDuration;
- final int originDuration;
- final int opacityDuration;
-
- radiusDuration = getRadiusDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
+ private void startHardwareEnter() {
+ if (mForceSoftware) { return; }
+ mPropX = CanvasProperty.createFloat(getCurrentX());
+ mPropY = CanvasProperty.createFloat(getCurrentY());
+ mPropRadius = CanvasProperty.createFloat(getCurrentRadius());
+ final Paint paint = mOwner.getRipplePaint();
+ mPropPaint = CanvasProperty.createPaint(paint);
- final float startX = getCurrentX();
- final float startY = getCurrentY();
- final float startRadius = getCurrentRadius();
-
- p.setAlpha((int) (p.getAlpha() * mOpacity + 0.5f));
-
- mPropPaint = CanvasProperty.createPaint(p);
- mPropRadius = CanvasProperty.createFloat(startRadius);
- mPropX = CanvasProperty.createFloat(startX);
- mPropY = CanvasProperty.createFloat(startY);
+ final int radiusDuration = getRadiusDuration();
final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius);
radius.setDuration(radiusDuration);
radius.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(radius);
final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
- x.setDuration(originDuration);
+ x.setDuration(radiusDuration);
x.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(x);
final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
- y.setDuration(originDuration);
+ y.setDuration(radiusDuration);
y.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(y);
+
+ final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
+ RenderNodeAnimator.PAINT_ALPHA, paint.getAlpha());
+ opacity.setDuration(OPACITY_ENTER_DURATION);
+ opacity.setInterpolator(LINEAR_INTERPOLATOR);
+ opacity.setStartValue(0);
+ mPendingHwAnimators.add(opacity);
+
+ invalidateSelf();
+ }
+
+ private void startHardwareExit() {
+ // Only run a hardware exit if we had a hardware enter to continue from
+ if (mForceSoftware || mPropPaint == null) return;
final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
RenderNodeAnimator.PAINT_ALPHA, 0);
- opacity.setDuration(opacityDuration);
+ opacity.setDuration(OPACITY_EXIT_DURATION);
opacity.setInterpolator(LINEAR_INTERPOLATOR);
opacity.addListener(mAnimationListener);
+ opacity.setStartDelay(computeFadeOutDelay());
+ mPendingHwAnimators.add(opacity);
+ invalidateSelf();
+ }
+
+ /**
+ * Starts a ripple enter animation.
+ */
+ public final void enter() {
+ mEnterStartedAtMillis = AnimationUtils.currentAnimationTimeMillis();
+ startSoftwareEnter();
+ startHardwareEnter();
+ }
- final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
- set.add(radius);
- set.add(opacity);
- set.add(x);
- set.add(y);
+ /**
+ * Starts a ripple exit animation.
+ */
+ public final void exit() {
+ startSoftwareExit();
+ startHardwareExit();
+ }
- return set;
+ private float getCurrentX() {
+ return MathUtils.lerp(mClampedStartingX - mBounds.exactCenterX(), mTargetX, mTweenX);
}
- @Override
- protected void jumpValuesToExit() {
- mOpacity = 0;
- mTweenX = 1;
- mTweenY = 1;
- mTweenRadius = 1;
+ private float getCurrentY() {
+ return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
+ }
+
+ private int getRadiusDuration() {
+ final float remainingRadius = mTargetRadius - getCurrentRadius();
+ return (int) (1000 * Math.sqrt(remainingRadius / WAVE_TOUCH_DOWN_ACCELERATION *
+ mDensityScale) + 0.5);
+ }
+
+ private float getCurrentRadius() {
+ return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius);
+ }
+
+ /**
+ * Draws the ripple to the canvas, inheriting the paint's color and alpha
+ * properties.
+ *
+ * @param c the canvas to which the ripple should be drawn
+ * @param p the paint used to draw the ripple
+ */
+ public void draw(Canvas c, Paint p) {
+ final boolean hasDisplayListCanvas = !mForceSoftware && c instanceof DisplayListCanvas;
+
+ pruneSwFinished();
+ if (hasDisplayListCanvas) {
+ final DisplayListCanvas hw = (DisplayListCanvas) c;
+ drawHardware(hw, p);
+ } else {
+ drawSoftware(c, p);
+ }
}
/**
@@ -319,10 +382,39 @@ class RippleForeground extends RippleComponent {
}
}
+ /**
+ * Ends all animations, jumping values to the end state.
+ */
+ public void end() {
+ for (int i = 0; i < mRunningSwAnimators.size(); i++) {
+ mRunningSwAnimators.get(i).end();
+ }
+ mRunningSwAnimators.clear();
+ for (int i = 0; i < mRunningHwAnimators.size(); i++) {
+ mRunningHwAnimators.get(i).end();
+ }
+ mRunningHwAnimators.clear();
+ }
+
+ private void onAnimationPropertyChanged() {
+ if (!mUsingProperties) {
+ invalidateSelf();
+ }
+ }
+
private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
mHasFinishedExit = true;
+ pruneHwFinished();
+ pruneSwFinished();
+
+ if (mRunningHwAnimators.isEmpty()) {
+ mPropPaint = null;
+ mPropRadius = null;
+ mPropX = null;
+ mPropY = null;
+ }
}
};
@@ -361,7 +453,7 @@ class RippleForeground extends RippleComponent {
@Override
public void setValue(RippleForeground object, float value) {
object.mTweenRadius = value;
- object.invalidateSelf();
+ object.onAnimationPropertyChanged();
}
@Override
@@ -375,18 +467,18 @@ class RippleForeground extends RippleComponent {
*/
private static final FloatProperty<RippleForeground> TWEEN_ORIGIN =
new FloatProperty<RippleForeground>("tweenOrigin") {
- @Override
- public void setValue(RippleForeground object, float value) {
- object.mTweenX = value;
- object.mTweenY = value;
- object.invalidateSelf();
- }
+ @Override
+ public void setValue(RippleForeground object, float value) {
+ object.mTweenX = value;
+ object.mTweenY = value;
+ object.onAnimationPropertyChanged();
+ }
- @Override
- public Float get(RippleForeground object) {
- return object.mTweenX;
- }
- };
+ @Override
+ public Float get(RippleForeground object) {
+ return object.mTweenX;
+ }
+ };
/**
* Property for animating opacity between 0 and its target value.
@@ -396,7 +488,7 @@ class RippleForeground extends RippleComponent {
@Override
public void setValue(RippleForeground object, float value) {
object.mOpacity = value;
- object.invalidateSelf();
+ object.onAnimationPropertyChanged();
}
@Override
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index bec22c934fc6..4c150c8f8a23 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -146,6 +146,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(call all-java-files-under, src/android) \
$(call all-java-files-under, ../test-runner/src/android) \
+ $(call all-java-files-under, ../test-mock/src/android) \
$(call all-java-files-under, src/com)
LOCAL_MODULE := legacy-android-test
LOCAL_NO_STANDARD_LIBRARIES := true
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 24ce1e4c23d0..5c577aecca28 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -2,9 +2,10 @@ cc_defaults {
name: "hwui_defaults",
defaults: [
"hwui_static_deps",
- "skia_deps"
+ "skia_deps",
//"hwui_bugreport_font_cache_usage",
//"hwui_compile_for_perf",
+ "hwui_pgo",
],
cpp_std: "c++17",
@@ -109,6 +110,22 @@ cc_defaults {
include_dirs: ["frameworks/native/opengl/libs/GLES2"],
}
+// Build libhwui with PGO by default.
+// Location of PGO profile data is defined in build/soong/cc/pgo.go
+// and is separate from hwui.
+// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
+// or set enable_profile_use property to false.
+cc_defaults {
+ name: "hwui_pgo",
+
+ pgo: {
+ instrumentation: true,
+ profile_file: "hwui/hwui.profdata",
+ benchmarks: ["hwui"],
+ enable_profile_use: false,
+ },
+}
+
// ------------------------
// library
// ------------------------
@@ -255,18 +272,6 @@ cc_library {
// Has moderate overhead
"hwui_enable_opengl_validation",
],
-
- // Build libhwui with PGO by default.
- // Location of PGO profile data is defined in build/soong/cc/pgo.go
- // and is separate from hwui.
- // To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
- // or set enable_profile_use property to false.
- pgo: {
- instrumentation: true,
- profile_file: "hwui/hwui.profdata",
- benchmarks: ["hwui"],
- enable_profile_use: false,
- },
}
// ------------------------
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 90fe0b74dcec..bad766c7d5ef 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -29,12 +29,6 @@ namespace android {
minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
const Typeface* typeface) {
const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
- minikin::FontStyle resolved = resolvedFace->fStyle;
-
- const minikin::FontVariant minikinVariant =
- (paint->getFontVariant() == minikin::FontVariant::ELEGANT)
- ? minikin::FontVariant::ELEGANT
- : minikin::FontVariant::COMPACT;
minikin::MinikinPaint minikinPaint;
/* Prepare minikin Paint */
@@ -46,7 +40,8 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
minikinPaint.wordSpacing = paint->getWordSpacing();
minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint);
minikinPaint.localeListId = paint->getMinikinLocaleListId();
- minikinPaint.fontStyle = minikin::FontStyle(minikinVariant, resolved.weight, resolved.slant);
+ minikinPaint.familyVariant = paint->getFamilyVariant();
+ minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
return minikinPaint;
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 76beb119b6da..002f75906c35 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -73,9 +73,9 @@ public:
uint32_t getMinikinLocaleListId() const { return mMinikinLocaleListId; }
- void setFontVariant(minikin::FontVariant variant) { mFontVariant = variant; }
+ void setFamilyVariant(minikin::FontFamily::Variant variant) { mFamilyVariant = variant; }
- minikin::FontVariant getFontVariant() const { return mFontVariant; }
+ minikin::FontFamily::Variant getFamilyVariant() const { return mFamilyVariant; }
void setHyphenEdit(uint32_t hyphen) { mHyphenEdit = hyphen; }
@@ -90,7 +90,7 @@ private:
float mWordSpacing = 0;
std::string mFontFeatureSettings;
uint32_t mMinikinLocaleListId;
- minikin::FontVariant mFontVariant;
+ minikin::FontFamily::Variant mFamilyVariant;
uint32_t mHyphenEdit = 0;
// The native Typeface object has the same lifetime of the Java Typeface
// object. The Java Paint object holds a strong reference to the Java Typeface
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 94492c52f365..ae9c475d09d4 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -24,7 +24,7 @@ Paint::Paint()
, mWordSpacing(0)
, mFontFeatureSettings()
, mMinikinLocaleListId(0)
- , mFontVariant(minikin::FontVariant::DEFAULT) {}
+ , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
Paint::Paint(const Paint& paint)
: SkPaint(paint)
@@ -32,7 +32,7 @@ Paint::Paint(const Paint& paint)
, mWordSpacing(paint.mWordSpacing)
, mFontFeatureSettings(paint.mFontFeatureSettings)
, mMinikinLocaleListId(paint.mMinikinLocaleListId)
- , mFontVariant(paint.mFontVariant)
+ , mFamilyVariant(paint.mFamilyVariant)
, mHyphenEdit(paint.mHyphenEdit)
, mTypeface(paint.mTypeface) {}
@@ -42,7 +42,7 @@ Paint::Paint(const SkPaint& paint)
, mWordSpacing(0)
, mFontFeatureSettings()
, mMinikinLocaleListId(0)
- , mFontVariant(minikin::FontVariant::DEFAULT) {}
+ , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
Paint::~Paint() {}
@@ -52,7 +52,7 @@ Paint& Paint::operator=(const Paint& other) {
mWordSpacing = other.mWordSpacing;
mFontFeatureSettings = other.mFontFeatureSettings;
mMinikinLocaleListId = other.mMinikinLocaleListId;
- mFontVariant = other.mFontVariant;
+ mFamilyVariant = other.mFamilyVariant;
mHyphenEdit = other.mHyphenEdit;
mTypeface = other.mTypeface;
return *this;
@@ -62,7 +62,8 @@ bool operator==(const Paint& a, const Paint& b) {
return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) &&
a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing &&
a.mFontFeatureSettings == b.mFontFeatureSettings &&
- a.mMinikinLocaleListId == b.mMinikinLocaleListId && a.mFontVariant == b.mFontVariant &&
- a.mHyphenEdit == b.mHyphenEdit && a.mTypeface == b.mTypeface;
+ a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
+ a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
+ a.mTypeface == b.mTypeface;
}
} // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index e527adc08fd7..ebc14c8b675b 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -45,7 +45,7 @@ static Typeface::Style computeAPIStyle(int weight, bool italic) {
static minikin::FontStyle computeMinikinStyle(int weight, bool italic) {
return minikin::FontStyle(uirenderer::MathUtils::clamp(weight, 1, 1000),
- static_cast<minikin::FontSlant>(italic));
+ static_cast<minikin::FontStyle::Slant>(italic));
}
// Resolve the relative weight from the baseWeight and target style.
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 1fcc028b890f..66d6f527e604 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -81,39 +81,39 @@ TEST(TypefaceTest, resolveDefault_and_setDefaultTest) {
TEST(TypefaceTest, createWithDifferentBaseWeight) {
std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, bold->fAPIStyle);
std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300));
- EXPECT_EQ(300, light->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, light->fStyle.slant);
+ EXPECT_EQ(300, light->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, light->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, light->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_fromRegular) {
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, Typeface::kItalic));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(Typeface::createRelative(nullptr, Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -123,30 +123,30 @@ TEST(TypefaceTest, createRelativeTest_BoldBase) {
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(700, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(700, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(1000, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(1000, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(700, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(700, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(1000, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(1000, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -156,30 +156,30 @@ TEST(TypefaceTest, createRelativeTest_LightBase) {
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(300, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(300, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(600, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(600, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.ITLIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(300, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(300, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(600, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(600, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -189,22 +189,22 @@ TEST(TypefaceTest, createRelativeTest_fromBoldStyled) {
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
// Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
// Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -212,8 +212,8 @@ TEST(TypefaceTest, createRelativeTest_fromBoldStyled) {
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -224,23 +224,23 @@ TEST(TypefaceTest, createRelativeTest_fromItalicStyled) {
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT,
// Typeface.ITALIC), Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
// Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -248,8 +248,8 @@ TEST(TypefaceTest, createRelativeTest_fromItalicStyled) {
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -261,8 +261,8 @@ TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) {
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java,
@@ -270,8 +270,8 @@ TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) {
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
@@ -279,8 +279,8 @@ TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) {
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -289,8 +289,8 @@ TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) {
// Typeface.create(typeface, Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -300,8 +300,8 @@ TEST(TypefaceTest, createAbsolute) {
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false)
// .build();
std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false));
- EXPECT_EQ(400, regular->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java,
@@ -309,8 +309,8 @@ TEST(TypefaceTest, createAbsolute) {
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false)
// .build();
std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
@@ -318,8 +318,8 @@ TEST(TypefaceTest, createAbsolute) {
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true)
// .build();
std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -327,8 +327,8 @@ TEST(TypefaceTest, createAbsolute) {
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true)
// .build();
std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
// In Java,
@@ -336,8 +336,8 @@ TEST(TypefaceTest, createAbsolute) {
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true)
// .build();
std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false));
- EXPECT_EQ(1000, over1000->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+ EXPECT_EQ(1000, over1000->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
}
@@ -346,24 +346,24 @@ TEST(TypefaceTest, createFromFamilies_Single) {
// Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
std::unique_ptr<Typeface> regular(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
- EXPECT_EQ(400, regular->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
std::unique_ptr<Typeface> bold(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
std::unique_ptr<Typeface> italic(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -371,8 +371,8 @@ TEST(TypefaceTest, createFromFamilies_Single) {
// Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
std::unique_ptr<Typeface> boldItalic(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -380,8 +380,8 @@ TEST(TypefaceTest, createFromFamilies_Single) {
// Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
std::unique_ptr<Typeface> over1000(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
- EXPECT_EQ(1000, over1000->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+ EXPECT_EQ(1000, over1000->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
}
@@ -389,30 +389,30 @@ TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) {
// In Java, new Typeface.Builder("Roboto-Regular.ttf").build();
std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(400, regular->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
makeSingleFamlyVector(kRobotoBold), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
makeSingleFamlyVector(kRobotoItalic), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
std::unique_ptr<Typeface> boldItalic(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic),
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
}
@@ -422,8 +422,8 @@ TEST(TypefaceTest, createFromFamilies_Family) {
buildFamily(kRobotoBoldItalic)};
std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(400, typeface->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+ EXPECT_EQ(400, typeface->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
@@ -431,8 +431,8 @@ TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic)};
std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(700, typeface->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+ EXPECT_EQ(700, typeface->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
} // namespace
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index ce4184967026..faea9b21311d 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -110,6 +110,12 @@ public:
void end(long long token);
/**
+ * Returns how many bytes are buffered in ProtoOutputStream.
+ * Notice, this is not the actual(compact) size of the output data.
+ */
+ size_t bytesWritten();
+
+ /**
* 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.
*/
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 9d8ee729a80d..1904d40ea67a 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -295,6 +295,12 @@ ProtoOutputStream::end(long long token)
}
}
+size_t
+ProtoOutputStream::bytesWritten()
+{
+ return mBuffer.size();
+}
+
bool
ProtoOutputStream::compact() {
if (mCompact) return true;
diff --git a/location/Android.mk b/location/Android.mk
index feeb8ce4a2aa..50509c6f1a3c 100644
--- a/location/Android.mk
+++ b/location/Android.mk
@@ -14,4 +14,4 @@
LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under, $(LOCAL_PATH))
+include $(call all-subdir-makefiles, $(LOCAL_PATH)) \ No newline at end of file
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index 902cd96067e9..73b2bb51a868 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -10,5 +10,13 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksLocationTests
-include $(BUILD_PACKAGE)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ core-test-rules \
+ guava \
+ mockito-target-minus-junit4 \
+ frameworks-base-testutils \
+ truth-prebuilt \
+LOCAL_COMPATIBILITY_SUITE := device-tests
+include $(BUILD_PACKAGE)
diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml
index 1d9df0f93a05..ddb8ea6aa53a 100644
--- a/location/tests/locationtests/AndroidManifest.xml
+++ b/location/tests/locationtests/AndroidManifest.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -23,13 +23,13 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
+
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.locationtests"
- android:label="Frameworks Location Tests" />
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.locationtests"
+ android:label="Frameworks Location Tests" />
</manifest>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
new file mode 100644
index 000000000000..0c5b7cca5ae5
--- /dev/null
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks Location API Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksLocationTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksLocationTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.locationtests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/location/tests/locationtests/src/android/location/GnssStatusTest.java b/location/tests/locationtests/src/android/location/GnssStatusTest.java
new file mode 100644
index 000000000000..79ea0d61b799
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssStatusTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link GnssStatus}.
+ */
+@SmallTest
+public class GnssStatusTest extends TestCase {
+
+ private static final String TAG = GnssStatusTest.class.getSimpleName();
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /*
+ * Create {@link GnssStatus} with default value, verify whether its fields are set correctly.
+ *
+ */
+ public void testEmptyGnssStatus() throws Exception {
+ Log.i(TAG, "testEmptyGnssStatus");
+ List<SatelliteInfo> svInfos = new ArrayList<>();
+ GnssStatus gnssStatus = createGnssStatus(svInfos);
+ verifyGnssStatus(svInfos, gnssStatus);
+ }
+
+ /*
+ * Create {@link GnssStatus} with only one satellite info, verify whether its fields are set
+ * correctly.
+ */
+ public void testOneSatelliteGnssStatus() throws Exception {
+ Log.i(TAG, "testOneSatelliteGnssStatus");
+ List<SatelliteInfo> svInfos = new ArrayList<>();
+ SatelliteInfo svInfo =
+ new SatelliteInfo(100,1, true, true, true, true, 100f, 20.3f, 45.5f, 100.23f);
+ svInfos.add(svInfo);
+ GnssStatus gnssStatus = createGnssStatus(svInfos);
+ verifyGnssStatus(svInfos, gnssStatus);
+ }
+
+ /*
+ * Create {@link GnssStatus} with multiple satellite info, verify whether its fields are set
+ * correctly.
+ */
+ public void testMultipleSatellitesGnssStatus() throws Exception {
+ Log.i(TAG, "testMultipleSatellitesGnssStatus");
+ List<SatelliteInfo> svInfos = new ArrayList<>();
+ SatelliteInfo svInfo1 =
+ new SatelliteInfo(20, 1,true, true, true, true, 10.1f, 20.3f, 45.5f, 111.23f);
+ SatelliteInfo svInfo2 =
+ new SatelliteInfo(50, 2, true, false, true, false, 20.2f, 21.3f, 46.5f, 222.23f);
+ SatelliteInfo svInfo3 =
+ new SatelliteInfo(192, 3, false, true, false, true, 30.3f, 22.3f, 47.5f, 333.23f);
+ SatelliteInfo svInfo4 =
+ new SatelliteInfo(250, 4, false, false, false, false, 40.4f, 23.3f, 48.5f, 444.23f);
+ svInfos.add(svInfo1);
+ svInfos.add(svInfo2);
+ svInfos.add(svInfo3);
+ svInfos.add(svInfo4);
+ GnssStatus gnssStatus = createGnssStatus(svInfos);
+ verifyGnssStatus(svInfos, gnssStatus);
+ }
+
+ private void verifyGnssStatus(List<SatelliteInfo> svInfos, GnssStatus gnssStatus) {
+ Log.i(TAG, String.format("Verifing {0} satellites info.",svInfos.size()));
+ assertEquals(TAG + "::SatelliteCount", svInfos.size(),
+ gnssStatus.getSatelliteCount());
+ for (int i = 0; i< svInfos.size(); i++) {
+ SatelliteInfo svInfo = svInfos.get(i);
+ assertEquals(TAG + "::Svid", svInfo.mSvid, gnssStatus.getSvid(i));
+ assertEquals(TAG + "::ConstellationType", svInfo.mConstellationType,
+ gnssStatus.getConstellationType(i));
+ assertEquals(TAG + "::Cn0DbHz", svInfo.mCn0DbHz, gnssStatus.getCn0DbHz(i));
+ assertEquals(TAG + "::Elevation", svInfo.mElevation,
+ gnssStatus.getElevationDegrees(i));
+ assertEquals(TAG + "::Azimuth", svInfo.mAzimuth, gnssStatus.getAzimuthDegrees(i));
+ assertEquals(TAG + "::CarrierFrequencyHz", svInfo.mCarrierFrequency,
+ gnssStatus.getCarrierFrequencyHz(i));
+ assertEquals(TAG + "::hasEphemerisData", svInfo.mHasEphemris,
+ gnssStatus.hasEphemerisData(i));
+ assertEquals(TAG + "::HasAlmanacData", svInfo.mHasAlmanac,
+ gnssStatus.hasAlmanacData(i));
+ assertEquals(TAG + "::UsedInFix", svInfo.mUsedInFix, gnssStatus.usedInFix(i));
+ assertEquals(TAG + "::HasCarrierFrequencyHz", svInfo.mHasCarriesFrequency,
+ gnssStatus.hasCarrierFrequencyHz(i));
+ }
+ }
+
+ private static GnssStatus createGnssStatus(List<SatelliteInfo> svInfos) throws Exception {
+ Class<?> intClass = Integer.TYPE;
+ Class<?> floatArrayClass = Class.forName("[F");
+ Class<?> intArrayClass = Class.forName("[I");
+ Class[] cArg = new Class[6];
+ cArg[0] = intClass;
+ cArg[1] = intArrayClass;
+ cArg[2] = floatArrayClass;
+ cArg[3] = floatArrayClass;
+ cArg[4] = floatArrayClass;
+ cArg[5] = floatArrayClass;
+ Constructor<GnssStatus> ctor = GnssStatus.class.getDeclaredConstructor(cArg);
+ ctor.setAccessible(true);
+ return ctor.newInstance(svInfos.size(),
+ SatelliteInfo.getSvidWithFlagsArray(svInfos),
+ SatelliteInfo.getCn0sArray(svInfos),
+ SatelliteInfo.getElevationsArray(svInfos),
+ SatelliteInfo.getAzimuthsArray(svInfos),
+ SatelliteInfo.getCarrierFrequencyArray(svInfos));
+ }
+}
diff --git a/location/tests/locationtests/src/android/location/GpsStatusTest.java b/location/tests/locationtests/src/android/location/GpsStatusTest.java
deleted file mode 100644
index 316e88d29709..000000000000
--- a/location/tests/locationtests/src/android/location/GpsStatusTest.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.location;
-
-import junit.framework.TestCase;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * Unit tests for {@link GpsStatus}.
- */
-@SmallTest
-public class GpsStatusTest extends TestCase {
-
- private static final int MAX_VALUE = 250;
-
- private final Random mRandom = new Random();
-
- private GpsStatus mStatus;
- private int mCount;
- private int[] mPrns;
- private float[] mCn0s;
- private float[] mElevations;
- private float[] mAzimuth;
- private int mEphemerisMask;
- private int mAlmanacMask;
- private int mUsedInFixMask;
-
- public void setUp() throws Exception {
- super.setUp();
- mStatus = createGpsStatus();
- generateSatellitesData(generateInt());
- }
-
- public void testEmptyGpsStatus() throws Exception {
- verifyIsEmpty(mStatus);
- }
-
- public void testGpsStatusIterator() throws Exception {
- generateSatellitesData(2);
- setSatellites(mStatus);
- Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator();
- assertTrue("hasNext(1)", iterator.hasNext());
- assertTrue("hasNext(1) does not overflow", iterator.hasNext());
- GpsSatellite satellite1 = iterator.next();
- assertNotNull("satellite", satellite1);
- assertTrue("hasNext(2)", iterator.hasNext());
- assertTrue("hasNext(2) does not overflow", iterator.hasNext());
- GpsSatellite satellite2 = iterator.next();
- assertNotNull("satellite", satellite2);
- assertFalse("hasNext() no elements", iterator.hasNext());
- }
-
- public void testTtff() throws Exception {
- int testTtff = generateInt();
- set(mStatus, testTtff);
- verifyTtff(mStatus, testTtff);
- }
-
- public void testCopyTtff() throws Exception {
- int testTtff = generateInt();
- verifyTtff(mStatus, 0);
-
- GpsStatus otherStatus = createGpsStatus();
- set(otherStatus, testTtff);
- verifyTtff(otherStatus, testTtff);
-
- set(mStatus, otherStatus);
- verifyTtff(mStatus, testTtff);
- }
-
- public void testSetSatellites() throws Exception {
- setSatellites(mStatus);
- verifySatellites(mStatus);
- }
-
- public void testCopySatellites() throws Exception {
- verifyIsEmpty(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testOverrideSatellites() throws Exception {
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(mCount, true /* reusePrns */);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testAddSatellites() throws Exception {
- int count = 10;
- generateSatellitesData(count);
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(count);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testAddMoreSatellites() throws Exception {
- int count = 25;
- generateSatellitesData(count);
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(count * 2);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testAddLessSatellites() throws Exception {
- int count = 25;
- generateSatellitesData(count * 2);
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(count);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- private static void verifyIsEmpty(GpsStatus status) {
- verifySatelliteCount(status, 0);
- verifyTtff(status, 0);
- }
-
- private static void verifySatelliteCount(GpsStatus status, int expectedCount) {
- int satellites = 0;
- for (GpsSatellite s : status.getSatellites()) {
- ++satellites;
- }
- assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites);
- }
-
- private void verifySatellites(GpsStatus status) {
- verifySatelliteCount(status, mCount);
- verifySatellites(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask,
- mAlmanacMask, mUsedInFixMask);
- }
-
- private static void verifySatellites(
- GpsStatus status,
- int count,
- int[] prns,
- float[] cn0s,
- float[] elevations,
- float[] azimuth,
- int ephemerisMask,
- int almanacMask,
- int usedInFixMask) {
- for (int i = 0; i < count; ++i) {
- int prn = prns[i];
- GpsSatellite satellite = getSatellite(status, prn);
- assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite);
- assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), cn0s[i], satellite.getSnr());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "Elevation"),
- elevations[i],
- satellite.getElevation());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "Azimuth"),
- azimuth[i],
- satellite.getAzimuth());
- int prnShift = 1 << (prn - 1);
- assertEquals(
- getSatelliteAssertInfo(i, prn, "ephemeris"),
- (ephemerisMask & prnShift) != 0,
- satellite.hasEphemeris());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "almanac"),
- (almanacMask & prnShift) != 0,
- satellite.hasAlmanac());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "usedInFix"),
- (usedInFixMask & prnShift) != 0,
- satellite.usedInFix());
- }
- }
-
- private static void verifyTtff(GpsStatus status, int expectedTtff) {
- assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix());
- }
-
- private static GpsStatus createGpsStatus() throws Exception {
- Constructor<GpsStatus> ctor = GpsStatus.class.getDeclaredConstructor();
- ctor.setAccessible(true);
- return ctor.newInstance();
- }
-
- private static void set(GpsStatus status, int ttff) throws Exception {
- Class<?> statusClass = status.getClass();
- Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE);
- setTtff.setAccessible(true);
- setTtff.invoke(status, ttff);
- }
-
- private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception {
- Class<?> statusClass = status.getClass();
- Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass);
- setStatus.setAccessible(true);
- setStatus.invoke(status, statusToSet);
- }
-
- private void setSatellites(GpsStatus status) throws Exception {
- set(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask,
- mUsedInFixMask);
- }
-
- private static void set(
- GpsStatus status,
- int count,
- int[] prns,
- float[] cn0s,
- float[] elevations,
- float[] azimuth,
- int ephemerisMask,
- int almanacMask,
- int usedInFixMask) throws Exception {
- Class<?> statusClass = status.getClass();
- Class<?> intClass = Integer.TYPE;
- Class<?> floatArrayClass = Class.forName("[F");
- Method setStatus = statusClass.getDeclaredMethod(
- "setStatus",
- intClass,
- Class.forName("[I"),
- floatArrayClass,
- floatArrayClass,
- floatArrayClass,
- intClass,
- intClass,
- intClass);
- setStatus.setAccessible(true);
- setStatus.invoke(
- status,
- count,
- prns,
- cn0s,
- elevations,
- azimuth,
- ephemerisMask,
- almanacMask,
- usedInFixMask);
- }
-
- private int generateInt() {
- return mRandom.nextInt(MAX_VALUE) + 1;
- }
-
- private int[] generateIntArray(int count) {
- Set<Integer> generatedPrns = new HashSet<>();
- int[] array = new int[count];
- for(int i = 0; i < count; ++i) {
- int generated;
- do {
- generated = generateInt();
- } while (generatedPrns.contains(generated));
- array[i] = generated;
- generatedPrns.add(generated);
- }
- return array;
- }
-
- private float[] generateFloatArray(int count) {
- float[] array = new float[count];
- for(int i = 0; i < count; ++i) {
- array[i] = generateInt();
- }
- return array;
- }
-
- private int generateMask(int[] prns) {
- int mask = 0;
- int prnsLength = prns.length;
- for (int i = 0; i < prnsLength; ++i) {
- if (mRandom.nextBoolean()) {
- mask |= 1 << (prns[i] - 1);
- }
- }
- return mask;
- }
-
- private void generateSatellitesData(int count) {
- generateSatellitesData(count, false /* reusePrns */);
- }
-
- private void generateSatellitesData(int count, boolean reusePrns) {
- mCount = count;
- if (!reusePrns) {
- mPrns = generateIntArray(count);
- }
- mCn0s = generateFloatArray(count);
- mElevations = generateFloatArray(count);
- mAzimuth = generateFloatArray(count);
- mEphemerisMask = generateMask(mPrns);
- mAlmanacMask = generateMask(mPrns);
- mUsedInFixMask = generateMask(mPrns);
- }
-
- private static GpsSatellite getSatellite(GpsStatus status, int prn) {
- for (GpsSatellite satellite : status.getSatellites()) {
- if (satellite.getPrn() == prn) {
- return satellite;
- }
- }
- return null;
- }
-
- private static String getSatelliteAssertInfo(int index, int prn, String param) {
- return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn);
- }
-}
diff --git a/location/tests/locationtests/src/android/location/SatelliteInfo.java b/location/tests/locationtests/src/android/location/SatelliteInfo.java
new file mode 100644
index 000000000000..b6453ef0eabc
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/SatelliteInfo.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 android.location;
+
+import java.util.List;
+
+/*
+ * Helper class to store single Satellite info, only used it in the unit test.
+ */
+public class SatelliteInfo {
+ private static final int SVID_MAX_BIT_INDEX = 32;
+ private static final int SVID_SHIFT_WIDTH = 8;
+ private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
+
+ // Index for the bits in mSvidWithFlag
+ private static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX = 0;
+ private static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX = 1;
+ private static final int GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX = 2;
+ private static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX = 3;
+ public int mSvid;
+ public int mSvidWithFlag;
+ public float mCn0DbHz;
+ public float mElevation;
+ public float mAzimuth;
+ public float mCarrierFrequency;
+
+ /*
+ * Flag fields, it stores the same information as svidWithFlag, but in different format, easy for
+ * the unit test.
+ */
+ public int mConstellationType;
+ public boolean mHasEphemris;
+ public boolean mHasAlmanac;
+ public boolean mUsedInFix;
+ public boolean mHasCarriesFrequency;
+
+ public SatelliteInfo(int svid, int constellationType, boolean hasEphemris, boolean hasAlmanac,
+ boolean usedInFix, boolean hasCarriesFrequency, float cn0, float elevation, float azimuth,
+ float carrierFrequency) {
+ mSvidWithFlag =
+ setRange(mSvidWithFlag, constellationType, CONSTELLATION_TYPE_SHIFT_WIDTH, SVID_SHIFT_WIDTH);
+ mSvidWithFlag = setRange(mSvidWithFlag, svid, SVID_SHIFT_WIDTH, SVID_MAX_BIT_INDEX);
+ mSvidWithFlag = setBit(mSvidWithFlag, hasEphemris, GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX);
+ mSvidWithFlag = setBit(mSvidWithFlag, hasAlmanac, GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX);
+ mSvidWithFlag = setBit(mSvidWithFlag, usedInFix, GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX);
+ mSvidWithFlag =
+ setBit(mSvidWithFlag, hasCarriesFrequency, GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX);
+ this.mSvid = svid;
+ this.mConstellationType = constellationType;
+ this.mCn0DbHz = cn0;
+ this.mElevation = elevation;
+ this.mAzimuth = azimuth;
+ this.mCarrierFrequency = carrierFrequency;
+ this.mHasEphemris = hasEphemris;
+ this.mHasAlmanac = hasAlmanac;
+ this.mUsedInFix = usedInFix;
+ this.mHasCarriesFrequency = hasCarriesFrequency;
+ }
+
+ /*
+ * Gernerate svidWithFlags array from svInfos
+ */
+ public static int[] getSvidWithFlagsArray(List<SatelliteInfo> svInfos) {
+ int[] svidWithFlags = new int[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ svidWithFlags[i] = svInfos.get(i).mSvidWithFlag;
+ }
+ return svidWithFlags;
+ }
+
+ /*
+ * Gernerate cn0s array from svInfos
+ */
+ public static float[] getCn0sArray(List<SatelliteInfo> svInfos) {
+ float[] cn0s = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ cn0s[i] = svInfos.get(i).mCn0DbHz;
+ }
+ return cn0s;
+ }
+
+ /*
+ * Gernerate elevations array from svInfos
+ */
+ public static float[] getElevationsArray(List<SatelliteInfo> svInfos) {
+ float[] elevations = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ elevations[i] = svInfos.get(i).mElevation;
+ }
+ return elevations;
+ }
+
+ /*
+ * Gernerate azimuths array from svInfos
+ */
+ public static float[] getAzimuthsArray(List<SatelliteInfo> svInfos) {
+ float[] azimuths = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ azimuths[i] = svInfos.get(i).mAzimuth;
+ }
+ return azimuths;
+ }
+
+ /*
+ * Gernerate carrierFrequency array from svInfos
+ */
+ public static float[] getCarrierFrequencyArray(List<SatelliteInfo> svInfos) {
+ float[] carrierFrequencies = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ carrierFrequencies[i] = svInfos.get(i).mCarrierFrequency;
+ }
+ return carrierFrequencies;
+ }
+
+ private int setBit(int targetValue, boolean value, int index) {
+ if (value) {
+ targetValue = targetValue | (1 << index);
+ } else {
+ targetValue = targetValue & ~(1 << index);
+ }
+ return targetValue;
+ }
+
+ /*
+ * Set the bit in the range [fromIndex, toIndex), index start from the lowest bit.
+ * value -> 1 1 0 1 1 0 1 0
+ * index -> 7 6 5 4 3 2 1 0
+ * This function will set the bit in the range to the lowest X bits of the value.
+ */
+ private int setRange(int targetValue, int value, int fromIndex, int toIndex) {
+ int rangeLen = toIndex - fromIndex;
+ int valueMask = (1 << rangeLen) -1;
+ value &= valueMask;
+ value = value << fromIndex;
+ valueMask = valueMask << fromIndex;
+ targetValue &= (~valueMask);
+ targetValue |= value;
+ return targetValue;
+ }
+
+} \ No newline at end of file
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 1feea8907cd4..12e5744d2035 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -91,10 +91,10 @@ import android.util.Log;
* are only decrypted when the samples are delivered to the decoder.
* <p>
* MediaDrm methods throw {@link android.media.MediaDrm.MediaDrmStateException}
- * when a method is called on a MediaDrm object that has had an unrecoverable failure
- * in the DRM plugin or security hardware.
- * {@link android.media.MediaDrm.MediaDrmStateException} extends
- * {@link java.lang.IllegalStateException} with the addition of a developer-readable
+ * when a method is called on a MediaDrm object that has had an unrecoverable failure
+ * in the DRM plugin or security hardware.
+ * {@link android.media.MediaDrm.MediaDrmStateException} extends
+ * {@link java.lang.IllegalStateException} with the addition of a developer-readable
* diagnostic information string associated with the exception.
* <p>
* In the event of a mediaserver process crash or restart while a MediaDrm object
@@ -102,9 +102,9 @@ import android.util.Log;
* To recover, the app must release the MediaDrm object, then create and initialize
* a new one.
* <p>
- * As {@link android.media.MediaDrmResetException} and
- * {@link android.media.MediaDrm.MediaDrmStateException} both extend
- * {@link java.lang.IllegalStateException}, they should be in an earlier catch()
+ * As {@link android.media.MediaDrmResetException} and
+ * {@link android.media.MediaDrm.MediaDrmStateException} both extend
+ * {@link java.lang.IllegalStateException}, they should be in an earlier catch()
* block than {@link java.lang.IllegalStateException} if handled separately.
* <p>
* <a name="Callbacks"></a>
@@ -165,7 +165,7 @@ public final class MediaDrm {
/**
* Query if the given scheme identified by its UUID is supported on
- * this device, and whether the drm plugin is able to handle the
+ * this device, and whether the DRM plugin is able to handle the
* media container format specified by mimeType.
* @param uuid The UUID of the crypto scheme.
* @param mimeType The MIME type of the media container, e.g. "video/mp4"
@@ -745,7 +745,7 @@ public final class MediaDrm {
* returned in KeyRequest.defaultUrl.
* <p>
* After the app has received the key request response from the server,
- * it should deliver to the response to the DRM engine plugin using the method
+ * it should deliver to the response to the MediaDrm instance using the method
* {@link #provideKeyResponse}.
*
* @param scope may be a sessionId or a keySetId, depending on the specified keyType.
@@ -781,7 +781,7 @@ public final class MediaDrm {
/**
* A key response is received from the license server by the app, then it is
- * provided to the DRM engine plugin using provideKeyResponse. When the
+ * provided to the MediaDrm instance using provideKeyResponse. When the
* response is for an offline key request, a keySetId is returned that can be
* used to later restore the keys to a new session with the method
* {@link #restoreKeys}.
@@ -829,7 +829,7 @@ public final class MediaDrm {
* in the form of {name, value} pairs. Since DRM license policies vary by vendor,
* the specific status field names are determined by each DRM vendor. Refer to your
* DRM provider documentation for definitions of the field names for a particular
- * DRM engine plugin.
+ * DRM plugin.
*
* @param sessionId the session ID for the DRM session
*/
@@ -897,11 +897,11 @@ public final class MediaDrm {
@NonNull String certAuthority);
/**
- * After a provision response is received by the app, it is provided to the DRM
- * engine plugin using this method.
+ * After a provision response is received by the app, it is provided to the
+ * MediaDrm instance using this method.
*
* @param response the opaque provisioning response byte array to provide to the
- * DRM engine plugin.
+ * MediaDrm instance.
*
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
@@ -912,7 +912,6 @@ public final class MediaDrm {
}
@NonNull
- /* could there be a valid response with 0-sized certificate or key? */
private native Certificate provideProvisionResponseNative(@NonNull byte[] response)
throws DeniedByServerException;
@@ -953,26 +952,26 @@ public final class MediaDrm {
/**
* Remove all secure stops without requiring interaction with the server.
*/
- public native void releaseAllSecureStops();
+ public native void releaseAllSecureStops();
/**
- * String property name: identifies the maker of the DRM engine plugin
+ * String property name: identifies the maker of the DRM plugin
*/
public static final String PROPERTY_VENDOR = "vendor";
/**
- * String property name: identifies the version of the DRM engine plugin
+ * String property name: identifies the version of the DRM plugin
*/
public static final String PROPERTY_VERSION = "version";
/**
- * String property name: describes the DRM engine plugin
+ * String property name: describes the DRM plugin
*/
public static final String PROPERTY_DESCRIPTION = "description";
/**
* String property name: a comma-separated list of cipher and mac algorithms
- * supported by CryptoSession. The list may be empty if the DRM engine
+ * supported by CryptoSession. The list may be empty if the DRM
* plugin does not support CryptoSession operations.
*/
public static final String PROPERTY_ALGORITHMS = "algorithms";
@@ -988,7 +987,7 @@ public final class MediaDrm {
public @interface StringProperty {}
/**
- * Read a DRM engine plugin String property value, given the property name string.
+ * Read a MediaDrm String property value, given the property name string.
* <p>
* Standard fields names are:
* {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
@@ -998,6 +997,13 @@ public final class MediaDrm {
public native String getPropertyString(@NonNull @StringProperty String propertyName);
/**
+ * Set a MediaDrm String property value, given the property name string
+ * and new value for the property.
+ */
+ public native void setPropertyString(@NonNull @StringProperty String propertyName,
+ @NonNull String value);
+
+ /**
* Byte array property name: the device unique identifier is established during
* device provisioning and provides a means of uniquely identifying each device.
*/
@@ -1011,7 +1017,7 @@ public final class MediaDrm {
public @interface ArrayProperty {}
/**
- * Read a DRM engine plugin byte array property value, given the property name string.
+ * Read a MediaDrm byte array property value, given the property name string.
* <p>
* Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
*/
@@ -1019,17 +1025,13 @@ public final class MediaDrm {
public native byte[] getPropertyByteArray(@ArrayProperty String propertyName);
/**
- * Set a DRM engine plugin String property value.
- */
- public native void setPropertyString(
- String propertyName, @NonNull String value);
-
- /**
- * Set a DRM engine plugin byte array property value.
- */
- public native void setPropertyByteArray(
+ * Set a MediaDrm byte array property value, given the property name string
+ * and new value for the property.
+ */
+ public native void setPropertyByteArray(@NonNull @ArrayProperty
String propertyName, @NonNull byte[] value);
+
private static final native void setCipherAlgorithmNative(
@NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
@@ -1158,7 +1160,7 @@ public final class MediaDrm {
* The algorithm string conforms to JCA Standard Names for Mac
* Algorithms and is case insensitive. For example "HmacSHA256".
* <p>
- * The list of supported algorithms for a DRM engine plugin can be obtained
+ * The list of supported algorithms for a DRM plugin can be obtained
* using the method {@link #getPropertyString} with the property name
* "algorithms".
*/
@@ -1272,7 +1274,7 @@ public final class MediaDrm {
* storage, and used when invoking the signRSA method.
*
* @param response the opaque certificate response byte array to provide to the
- * DRM engine plugin.
+ * MediaDrm instance.
*
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index bdc0fda6aca3..31eb948dcd09 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -34,6 +34,7 @@ import android.util.SparseArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
+import java.util.Objects;
/**
* Contains metadata about an item, such as the title, artist, etc.
@@ -616,6 +617,71 @@ public final class MediaMetadata implements Parcelable {
};
/**
+ * Compares the contents of this object to another MediaMetadata object. It
+ * does not compare Bitmaps and Ratings as the media player can choose to
+ * forgo these fields depending on how you retrieve the MediaMetadata.
+ *
+ * @param o The Metadata object to compare this object against
+ * @return Whether or not the two objects have matching fields (excluding
+ * Bitmaps and Ratings)
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof MediaMetadata)) {
+ return false;
+ }
+
+ final MediaMetadata m = (MediaMetadata) o;
+
+ for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
+ String key = METADATA_KEYS_TYPE.keyAt(i);
+ switch (METADATA_KEYS_TYPE.valueAt(i)) {
+ case METADATA_TYPE_TEXT:
+ if (!Objects.equals(getString(key), m.getString(key))) {
+ return false;
+ }
+ break;
+ case METADATA_TYPE_LONG:
+ if (getLong(key) != m.getLong(key)) {
+ return false;
+ }
+ break;
+ default:
+ // Ignore ratings and bitmaps when comparing
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 17;
+
+ for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
+ String key = METADATA_KEYS_TYPE.keyAt(i);
+ switch (METADATA_KEYS_TYPE.valueAt(i)) {
+ case METADATA_TYPE_TEXT:
+ hashCode = 31 * hashCode + Objects.hash(getString(key));
+ break;
+ case METADATA_TYPE_LONG:
+ hashCode = 31 * hashCode + Long.hashCode(getLong(key));
+ break;
+ default:
+ // Ignore ratings and bitmaps when comparing
+ break;
+ }
+ }
+
+ return hashCode;
+ }
+
+ /**
* Use to build MediaMetadata objects. The system defined metadata keys must
* use the appropriate data type.
*/
diff --git a/packages/CarrierDefaultApp/res/values-sw/strings.xml b/packages/CarrierDefaultApp/res/values-sw/strings.xml
index c546fcee6186..a52a73335276 100644
--- a/packages/CarrierDefaultApp/res/values-sw/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-sw/strings.xml
@@ -5,7 +5,7 @@
<string name="android_system_label" msgid="2797790869522345065">"Mtoa Huduma za Simu"</string>
<string name="portal_notification_id" msgid="5155057562457079297">"Data ya mtandao wa simu imekwisha"</string>
<string name="no_data_notification_id" msgid="668400731803969521">"Data yako ya mtandao wa simu imezimwa"</string>
- <string name="portal_notification_detail" msgid="2295729385924660881">"Gonga ili utembelee tovuti ya %s"</string>
+ <string name="portal_notification_detail" msgid="2295729385924660881">"Gusa ili utembelee tovuti ya %s"</string>
<string name="no_data_notification_detail" msgid="3112125343857014825">"Tafadhali wasiliana na mtoa huduma wako %s"</string>
<string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Hakuna muunganisho wa data kwa simu za mkononi"</string>
<string name="no_mobile_data_connection" msgid="544980465184147010">"Ongeza mpango wa mitandao mingine au data kupitia %s"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index ec3425338b06..e0a979dd6a2d 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -24,7 +24,7 @@
<string name="wifi_security_none" msgid="7985461072596594400">"لا شيء"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"معطلة"</string>
- <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏أخفقت تهيئة عنوان IP"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏تعذّرت تهيئة عنوان IP"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"الجهاز غير متصل بسبب انخفاض جودة الشبكة"</string>
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"‏تعذّر اتصال WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"حدثت مشكلة في المصادقة"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index bb5d2d8e3380..1b78d30e13c4 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Impossible d\'établir l\'association avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g> en raison d\'un NIP ou d\'une clé d\'accès incorrects."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Impossible d\'établir la communication avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Association refusée par <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordinateur"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Écouteurs"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Téléphone"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imagerie"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Écouteurs"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Périphérique d\'entrée"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi désactivé."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi déconnecté."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi : une barre."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec audio Bluetooth LDAC : qualité de lecture"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Sélectionner le codec audio Bluetooth LDAC :\nQualité de lecture"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Diffusion : <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS par TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Si cette option est activée, essayez le DNS par TLS sur le port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privé"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionnez le mode DNS privé"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Désactivé"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportuniste"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'hôte du fournisseur DNS privé"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Entrez le nom d\'hôte du fournisseur DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si cette option est activée, le passage du Wi-Fi aux données cellulaires est forcé lorsque le signal Wi-Fi est faible"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 308e97d81f7c..b66f95f4ce67 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -48,7 +48,7 @@
<string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
<string name="speed_label_medium" msgid="3175763313268941953">"普通"</string>
<string name="speed_label_fast" msgid="7715732164050975057">"速い"</string>
- <string name="speed_label_very_fast" msgid="2265363430784523409">"とても速い"</string>
+ <string name="speed_label_very_fast" msgid="2265363430784523409">"非常に速い"</string>
<string name="preference_summary_default_combination" msgid="8532964268242666060">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="6557104142667339895">"切断"</string>
<string name="bluetooth_disconnecting" msgid="8913264760027764974">"切断中..."</string>
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PINまたはパスキーが正しくないため、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>をペアに設定できませんでした。"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>と通信できません。"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ペア設定が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に拒否されました。"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"コンピュータ"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ヘッドセット"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"スマートフォン"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"画像"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ヘッドフォン"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"入力用周辺機器"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-FiはOFFです。"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fiが切断されました。"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fiはレベル1です。"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth オーディオ LDAC コーデック: 再生音質"</string>
<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 over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"この設定を有効にした場合、ポート 853 で DNS over TLS を試行します。"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"プライベート DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"プライベート DNS モードを選択"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"OFF"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"日和見"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"プライベート DNS プロバイダのホスト名"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS プロバイダのホスト名を入力"</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">"ON にすると、Wi-Fi の電波強度が弱い場合は強制的にモバイルデータ接続に切り替わるようになります"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 4e3bc3368aed..5a9bbc54dc20 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN 또는 패스키가 잘못되어 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 페어링하지 못했습니다."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 통신할 수 없습니다."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 페어링을 거부했습니다."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"컴퓨터"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"헤드셋"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"전화"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"이미징"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"헤드폰"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"입력 주변기기"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"블루투스"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi가 꺼져 있습니다."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 연결이 끊어졌습니다."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 신호 막대가 한 개입니다."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"블루투스 오디오 LDAC 코덱: 재생 품질"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"블루투스 오디오 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">"TLS를 통한 DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"사용 설정한 경우, 포트 853에서 TLS를 통해 DNS를 시도합니다."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"비공개 DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"비공개 DNS 모드 선택"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"사용 안함"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"기회주의적"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"비공개 DNS 제공업체 호스트 이름"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS 제공업체의 호스트 이름 입력"</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-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index a1beef68d8cd..2b31bf112c88 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -316,7 +316,7 @@
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"Ruhusu uwezo wa kutumia madirisha ya majaribio yenye muundo huru."</string>
<string name="local_backup_password_title" msgid="3860471654439418822">"Nenosiri la hifadhi rudufu ya eneo kazi"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string>
- <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gonga ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
+ <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
<string name="local_backup_password_toast_success" msgid="582016086228434290">"Nenosiri jipya la hifadhi rudufu limewekwa"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nenosiri jipya na uthibitisho havioani"</string>
<string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Imeshindwa kuweka nenosiri la hifadhi rudufu"</string>
@@ -331,8 +331,8 @@
<item msgid="5363960654009010371">"Rangi zilizoboreshwa kwa ajili ya maudhui dijitali"</item>
</string-array>
<string name="inactive_apps_title" msgid="1317817863508274533">"Programu zilizozimwa"</string>
- <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gonga ili ugeuze."</string>
- <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gonga ili ugeuze."</string>
+ <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gusa ili ugeuze."</string>
+ <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gusa ili ugeuze."</string>
<string name="runningservices_settings_title" msgid="8097287939865165213">"Huduma zinazoendeshwa"</string>
<string name="runningservices_settings_summary" msgid="854608995821032748">"Onyesha na dhibiti huduma zinazoendeshwa kwa sasa"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Utekelezaji wa WebView"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 5ff39b6bcf97..2db6178ba4e6 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ไม่สามารถจับคู่กับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ได้เพราะ PIN หรือรหัสผ่านไม่ถูกต้อง"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ไม่สามารถเชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ปฏิเสธการจับคู่อุปกรณ์"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"คอมพิวเตอร์"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ชุดหูฟัง"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"โทรศัพท์"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"การถ่ายภาพ"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"หูฟัง"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"อุปกรณ์อินพุต"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"บลูทูธ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi ปิดอยู่"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"สัญญาณ Wi-Fi 1 ขีด"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"เลือกตัวแปลงรหัสเสียงบลูทูธที่ใช้ 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="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS ส่วนตัว"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"เลือกโหมด DNS ส่วนตัว"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ปิด"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ที่ใช้โอกาส"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"ชื่อโฮสต์ของผู้ให้บริการ DNS ส่วนตัว"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ป้อนชื่อโฮสต์ของผู้ให้บริการ DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"เมื่อเปิดใช้แล้ว Wi-Fi จะส่งผ่านการเชื่อมต่อข้อมูลไปยังเครือข่ายมือถือเมื่อสัญญาณ Wi-Fi อ่อน"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 25543886e1b2..2215fe551ff0 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密碼金鑰不正確。"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"電腦"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"免持耳機"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"電話"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"顯像裝置"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"頭戴式耳機"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"周邊輸入裝置"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"藍牙"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 連線已中斷。"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 訊號強度一格。"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"藍牙音訊 LDAC 轉碼器:播放品質"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"選取藍牙音訊 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">"透過傳輸層安全標準 (TLS) 執行 DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"如果啟用這個選項,即可在通訊埠 853 上嘗試透過傳輸層安全標準 (TLS) 執行 DNS。"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"私人 DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"選取私人 DNS 模式"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"停用"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"隨機"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 供應商主機名稱"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入 DNS 供應商的主機名稱"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"啟用時,Wi-Fi 連線在訊號不穩的情況下會更積極轉換成行動數據連線"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 4bb16ffe597e..d82db410e70e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -77,7 +77,7 @@
<!-- Summary for the network but no internet connection was detected. -->
<string name="wifi_no_internet_no_reconnect">Won\'t automatically connect</string>
<!-- Summary for the remembered network but no internet connection was detected. -->
- <string name="wifi_no_internet">No Internet access</string>
+ <string name="wifi_no_internet">No internet access</string>
<!-- Summary for saved networks -->
<string name="saved_network">Saved by <xliff:g id="name">%1$s</xliff:g></string>
@@ -95,7 +95,7 @@
<string name="certinstaller_package" translatable="false">com.android.certinstaller</string>
<!-- Summary for Connected wifi network without internet -->
- <string name="wifi_connected_no_internet">Connected, no Internet</string>
+ <string name="wifi_connected_no_internet">Connected, no internet</string>
<!-- Summary for networks failing to connect due to association rejection status 17, AP full -->
<string name="wifi_ap_unable_to_handle_new_sta">Access point temporarily full</string>
@@ -192,14 +192,14 @@
<!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
<string name="bluetooth_hid_profile_summary_connected">Connected to input device</string>
<!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (user role). [CHAR LIMIT=25]-->
- <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for Internet access</string>
+ <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for internet access</string>
<!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (NAP role). [CHAR LIMIT=25]-->
- <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local Internet connection with device</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local internet connection with device</string>
<!-- Bluetooth settings. Connection options screen. The summary
for the PAN checkbox preference that describes how checking it
will set the PAN profile as preferred. -->
- <string name="bluetooth_pan_profile_summary_use_for">Use for Internet access</string>
+ <string name="bluetooth_pan_profile_summary_use_for">Use for internet access</string>
<!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference that describes how checking it will set the map profile as preferred. -->
<string name="bluetooth_map_profile_summary_use_for">Use for map</string>
<!-- Bluetooth settings. Connection options screen. The summary for the sap checkbox preference that describes how checking it will set the sap profile as preferred. -->
@@ -732,6 +732,11 @@
<!-- UI debug setting: profile time taken by hardware acceleration to render apps [CHAR LIMIT=25] -->
<string name="track_frame_time">Profile GPU rendering</string>
+ <!-- UI debug setting: enable gpu debug layers [CHAR LIMIT=25] -->
+ <string name="enable_gpu_debug_layers">Enable GPU debug layers</string>
+ <!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] -->
+ <string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string>
+
<!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] -->
<string name="window_animation_scale_title">Window animation scale</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 41b205bc5761..ba1c9e3cc43b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -869,6 +869,15 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.WAIT_FOR_DEBUGGER,
GlobalSettingsProto.WAIT_FOR_DEBUGGER);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
+ GlobalSettingsProto.ENABLE_GPU_DEBUG_LAYERS);
+ dumpSetting(s, p,
+ Settings.Global.GPU_DEBUG_APP,
+ GlobalSettingsProto.GPU_DEBUG_APP);
+ dumpSetting(s, p,
+ Settings.Global.GPU_DEBUG_LAYERS,
+ GlobalSettingsProto.GPU_DEBUG_LAYERS);
// Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.LOW_POWER_MODE,
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 3f3dd2a97960..e4dcbeebf84d 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -24,10 +24,10 @@
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Tafadhali subiri…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Tutatuma ripoti ya hitilafu kwenye simu baada ya muda mfupi"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Chagua kushiriki ripoti ya hitilafu"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gonga ili ushiriki ripoti yako ya hitilafu"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Chagua kushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Ripoti za hitilafu zinajumuisha data kutoka faili za kumbukumbu mbalimbali zilizo kwenye mfumo, ambazo huenda zinajumuisha data ambayo unachukulia kuwa nyeti (kama vile matumizi ya programu na maelezo kuhusu data ilipo). Shiriki ripoti za hitilafu na watu na programu unazoamini pekee."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Usionyeshe tena"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 04ce7524bda8..839fb873113e 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -117,8 +117,8 @@
<item quantity="other">‏رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
<item quantity="one">‏رمز PUK لشريحة SIM غير صالح، ويتبقى لديك محاولة واحدة (<xliff:g id="NUMBER_0">%d</xliff:g>)، تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
</plurals>
- <string name="kg_password_pin_failed" msgid="8769990811451236223">"‏أخفقت عملية \"رقم التعريف الشخصي\" لشريحة SIM"</string>
- <string name="kg_password_puk_failed" msgid="1331621440873439974">"‏أخفقت عملية PUK لشريحة SIM"</string>
+ <string name="kg_password_pin_failed" msgid="8769990811451236223">"‏تعذّر إتمام عملية \"رقم التعريف الشخصي\" لشريحة SIM"</string>
+ <string name="kg_password_puk_failed" msgid="1331621440873439974">"‏تعذّر إتمام عملية PUK لشريحة SIM"</string>
<string name="kg_pin_accepted" msgid="7637293533973802143">"تم قبول الرمز"</string>
<string name="keyguard_carrier_default" msgid="4274828292998453695">"لا تتوفر خدمة."</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"تبديل أسلوب الإدخال"</string>
diff --git a/packages/SystemUI/res/values-ar/config.xml b/packages/SystemUI/res/values-ar/config.xml
index 5309563e3986..477f2198e4a6 100644
--- a/packages/SystemUI/res/values-ar/config.xml
+++ b/packages/SystemUI/res/values-ar/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b5ac31a07c37..d64211857d8c 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -55,15 +55,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"تم إنشاء الاتصال بالإنترنت عن طريق البلوتوث."</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"إعداد أسلوب الإدخال"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"لوحة مفاتيح فعلية"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"‏هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى جهاز USB؟"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"‏هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى ملحق USB؟"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"‏هل تريد فتح <xliff:g id="ACTIVITY">%1$s</xliff:g> عند توصيل جهاز USB هذا؟"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"‏هل تريد فتح <xliff:g id="ACTIVITY">%1$s</xliff:g> عند توصيل ملحق USB هذا؟"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"هل تريد فتح <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"هل تريد فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"‏لا يعمل أي تطبيق مثبت مع ملحق UEB هذا. مزيد من المعلومات عن هذا الملحق على <xliff:g id="URL">%1$s</xliff:g>."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"‏ملحق USB"</string>
<string name="label_view" msgid="6304565553218192990">"عرض"</string>
- <string name="always_use_device" msgid="1450287437017315906">"‏الاستخدام بشكل افتراضي لجهاز USB هذا"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"‏الاستخدام بشكل افتراضي لملحق USB هذا"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> دائمًا عند توصيل <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> دائمًا عند توصيل <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"‏هل تريد السماح بتصحيح أخطاء USB؟"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"‏الملف المرجعي الرئيسي لـ RSA في هذا الكمبيوتر هو:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"السماح دائمًا من هذا الكمبيوتر"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 989f6d1ee7f7..6e1feea3d5ef 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -51,21 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Connexion Bluetooth partagée"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurer les modes de saisie"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Clavier physique"</string>
- <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
- <skip />
- <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
- <skip />
- <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
- <skip />
- <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
- <skip />
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Aucune application installée compatible avec accessoire USB. En savoir plus sur <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accessoire USB"</string>
<string name="label_view" msgid="6304565553218192990">"Afficher"</string>
- <!-- no translation found for always_use_device (4015357883336738417) -->
- <skip />
- <!-- no translation found for always_use_accessory (3257892669444535154) -->
- <skip />
+ <string name="always_use_device" msgid="4015357883336738417">"Toujours ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> lorsque <xliff:g id="USB_DEVICE">%2$s</xliff:g> est connecté"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Toujours ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> lorsque <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> est connecté"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Autoriser le débogage USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Empreinte numérique de la clé RSA de l\'ordinateur : \n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Toujours autoriser sur cet ordinateur"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index d1bbe50585aa..5853d9317353 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -51,21 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth imefungwa"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Weka mbinu za ingizo"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Kibodi halisi"</string>
- <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
- <skip />
- <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
- <skip />
- <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
- <skip />
- <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
- <skip />
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Hakuna programu zilizosakinishwa zinazofanya kazi na kifaa hiki cha USB. Pata maelezo zaidi kuhusu kifaa hiki kwenye <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Kifaa cha Usb"</string>
<string name="label_view" msgid="6304565553218192990">"Ona"</string>
- <!-- no translation found for always_use_device (4015357883336738417) -->
- <skip />
- <!-- no translation found for always_use_accessory (3257892669444535154) -->
- <skip />
+ <string name="always_use_device" msgid="4015357883336738417">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_DEVICE">%2$s</xliff:g> inaunganishwa"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> inaunganishwa"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Ruhusu utatuaji wa USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Alama ya kidole ya kitufe cha RSA ya kompyuta ni:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Ruhusu kutoka kwenye kompyuta hii kila wakati"</string>
@@ -77,7 +71,7 @@
<string name="screenshot_saving_title" msgid="8242282144535555697">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"Picha ya skrini inahifadhiwa"</string>
<string name="screenshot_saved_title" msgid="6461865960961414961">"Picha ya skrini imenaswa."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Gonga ili utazame picha ya skrini uliyohifadhi."</string>
+ <string name="screenshot_saved_text" msgid="2685605830386712477">"Gusa ili utazame picha ya skrini uliyohifadhi."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Haikuweza kunasa picha ya skrini"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Hitilafu imetokea wakati wa kuhifadhi picha ya skrini."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Haina nafasi ya kutosha kuhifadhi picha ya skrini."</string>
@@ -363,7 +357,7 @@
<string name="zen_silence_introduction" msgid="3137882381093271568">"Hatua hii huzuia sauti na mitetemo YOTE, ikiwa ni pamoja na ile inayotokana na kengele, muziki, video na michezo."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>+"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizo za dharura sana ziko hapo chini"</string>
- <string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Gusa tena ili ufungue"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"Kifaa hiki kinasimamiwa na shirika lako"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"Kifaa hiki kinadhibitiwa na <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
@@ -500,11 +494,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Masafa ya ishara ya kampuni ya simu"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Zana za walio na matatizo ya kuona au kusikia"</string>
- <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gonga ili urejeshe."</string>
- <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gonga ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
- <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gonga ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
- <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s. Gonga ili uweke mtetemo."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Gonga ili usitishe."</string>
+ <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gusa ili urejeshe."</string>
+ <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
+ <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
+ <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s. Gusa ili uweke mtetemo."</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Gusa ili usitishe."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Inaonyesha %s ya vidhibiti vya sauti. Telezesha kidole juu ili uondoe."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Imeficha vidhibiti vya sauti"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kirekebishi cha kiolesura cha mfumo"</string>
@@ -700,9 +694,9 @@
<string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"Juu 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"Juu 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"Skrini nzima ya chini"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gonga mara mbili ili ubadilishe."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gonga mara mbili ili uongeze."</string>
- <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>. Gonga mara mbili ili uchague."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gusa mara mbili ili ubadilishe."</string>
+ <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gusa mara mbili ili uongeze."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>. Gusa mara mbili ili uchague."</string>
<string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Hamisha <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ondoa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> imeongezwa kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
@@ -776,7 +770,7 @@
<string name="qs_dnd_keep" msgid="1825009164681928736">"Usibadilishe"</string>
<string name="qs_dnd_replace" msgid="8019520786644276623">"Badilisha"</string>
<string name="running_foreground_services_title" msgid="381024150898615683">"Programu zinatumika chinichini"</string>
- <string name="running_foreground_services_msg" msgid="6326247670075574355">"Gonga ili upate maelezo kuhusu betri na matumizi ya data"</string>
+ <string name="running_foreground_services_msg" msgid="6326247670075574355">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
<string name="data_usage_disable_mobile" msgid="5116269981510015864">"Ungependa kuzima data ya mtandao wa simu?"</string>
<string name="touch_filtered_warning" msgid="8671693809204767551">"Kwa sababu programu nyingine inazuia ombi la ruhusa, hatuwezi kuthibitisha jibu lako katika Mipangilio."</string>
</resources>
diff --git a/packages/SystemUI/shared/Android.mk b/packages/SystemUI/shared/Android.mk
index e177d1d6fcb3..5f75f7d71584 100644
--- a/packages/SystemUI/shared/Android.mk
+++ b/packages/SystemUI/shared/Android.mk
@@ -30,7 +30,7 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := SharedDummyLib
+LOCAL_PACKAGE_NAME := SystemUISharedLib
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := SystemUISharedLib
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index d82b010f11d9..cc4bc58fbf9d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,8 +16,8 @@
package com.android.systemui.shared.recents;
-import android.graphics.Bitmap;
import android.graphics.Rect;
+import com.android.systemui.shared.system.GraphicBufferCompat;
/**
* Temporary callbacks into SystemUI.
@@ -25,9 +25,8 @@ import android.graphics.Rect;
interface ISystemUiProxy {
/**
- * Proxies SurfaceControl.screenshot().
+ * Proxies SurfaceControl.screenshotToBuffer().
*/
- Bitmap screenshot(in Rect sourceCrop, int width, int height,
- int minLayer, int maxLayer, boolean useIdentityTransform,
- int rotation);
+ GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
+ int maxLayer, boolean useIdentityTransform, int rotation);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
index 8e2a25c16e88..4834bb18f53c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
@@ -45,6 +45,11 @@ import java.util.List;
*/
public class RecentsTaskLoadPlan {
+ /** The set of conditions to preload tasks. */
+ public static class PreloadOptions {
+ public boolean loadTitles = true;
+ }
+
/** The set of conditions to load tasks. */
public static class Options {
public int runningTaskId = -1;
@@ -80,7 +85,8 @@ public class RecentsTaskLoadPlan {
* Note: Do not lock, since this can be calling back to the loader, which separately also drives
* this call (callers should synchronize on the loader before making this call).
*/
- public void preloadPlan(RecentsTaskLoader loader, int runningTaskId, int currentUserId) {
+ public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
+ int currentUserId) {
Resources res = mContext.getResources();
ArrayList<Task> allTasks = new ArrayList<>();
if (mRawTasks == null) {
@@ -110,9 +116,12 @@ public class RecentsTaskLoadPlan {
}
// Load the title, icon, and color
- String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
- String titleDescription = loader.getAndUpdateContentDescription(taskKey,
- t.taskDescription);
+ String title = opts.loadTitles
+ ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
+ : "";
+ String titleDescription = opts.loadTitles
+ ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
+ : "";
Drawable icon = isStackTask
? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
: null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
index 9a991cfa0699..0f68026c7464 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
@@ -32,6 +32,7 @@ import android.util.LruCache;
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.Options;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -155,7 +156,7 @@ public class RecentsTaskLoader {
int currentUserId) {
try {
Trace.beginSection("preloadPlan");
- plan.preloadPlan(this, runningTaskId, currentUserId);
+ plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
} finally {
Trace.endSection();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java
new file mode 100644
index 000000000000..0241c593b850
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java
@@ -0,0 +1,73 @@
+/*
+ * 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.utilities;
+
+import static android.os.Trace.TRACE_TAG_APP;
+
+/**
+ * Helper class for internal trace functions.
+ */
+public class AppTrace {
+
+ /**
+ * Begins a new async trace section with the given {@param key} and {@param cookie}.
+ */
+ public static void start(String key, int cookie) {
+ android.os.Trace.asyncTraceBegin(TRACE_TAG_APP, key, cookie);
+ }
+
+ /**
+ * Begins a new async trace section with the given {@param key}.
+ */
+ public static void start(String key) {
+ android.os.Trace.asyncTraceBegin(TRACE_TAG_APP, key, 0);
+ }
+
+ /**
+ * Ends an existing async trace section with the given {@param key}.
+ */
+ public static void end(String key) {
+ android.os.Trace.asyncTraceEnd(TRACE_TAG_APP, key, 0);
+ }
+
+ /**
+ * Ends an existing async trace section with the given {@param key} and {@param cookie}.
+ */
+ public static void end(String key, int cookie) {
+ android.os.Trace.asyncTraceEnd(TRACE_TAG_APP, key, cookie);
+ }
+
+ /**
+ * Begins a new trace section with the given {@param key}. Can be nested.
+ */
+ public static void beginSection(String key) {
+ android.os.Trace.beginSection(key);
+ }
+
+ /**
+ * Ends an existing trace section started in the last {@link #beginSection(String)}.
+ */
+ public static void endSection() {
+ android.os.Trace.endSection();
+ }
+
+ /**
+ * Traces a counter value.
+ */
+ public static void count(String name, int count) {
+ android.os.Trace.traceCounter(TRACE_TAG_APP, name, count);
+ }
+}
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
index cfd1f9a51ee9..0bd89a78cfda 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -37,6 +38,13 @@ public class BackgroundExecutor {
}
/**
+ * Runs the given {@param callable} on one of the background executor threads.
+ */
+ public <T> Future<T> submit(Callable<T> callable) {
+ return mExecutorService.submit(callable);
+ }
+
+ /**
* Runs the given {@param runnable} on one of the background executor threads.
*/
public Future<?> submit(Runnable runnable) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ChoreographerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ChoreographerCompat.java
new file mode 100644
index 000000000000..4d422bb8a2bd
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ChoreographerCompat.java
@@ -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.
+ */
+package com.android.systemui.shared.system;
+
+import static android.view.Choreographer.CALLBACK_INPUT;
+
+import android.view.Choreographer;
+
+/**
+ * Wraps the internal choreographer.
+ */
+public class ChoreographerCompat {
+
+ /**
+ * Posts an input callback to the choreographer.
+ */
+ public static void postInputFrame(Choreographer choreographer, Runnable runnable) {
+ choreographer.postCallback(CALLBACK_INPUT, runnable, null);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl
new file mode 100644
index 000000000000..f9450adcdf30
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+parcelable GraphicBufferCompat; \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java
new file mode 100644
index 000000000000..66b8fed1a48f
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java
@@ -0,0 +1,64 @@
+/*
+ * 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 android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Wraps the internal graphic buffer.
+ */
+public class GraphicBufferCompat implements Parcelable {
+
+ private GraphicBuffer mBuffer;
+
+ public GraphicBufferCompat(GraphicBuffer buffer) {
+ mBuffer = buffer;
+ }
+
+ public GraphicBufferCompat(Parcel in) {
+ mBuffer = GraphicBuffer.CREATOR.createFromParcel(in);
+ }
+
+ public Bitmap toBitmap() {
+ return mBuffer != null
+ ? Bitmap.createHardwareBitmap(mBuffer)
+ : null;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mBuffer.writeToParcel(dest, flags);
+ }
+
+ public static final Parcelable.Creator<GraphicBufferCompat> CREATOR
+ = new Parcelable.Creator<GraphicBufferCompat>() {
+ public GraphicBufferCompat createFromParcel(Parcel in) {
+ return new GraphicBufferCompat(in);
+ }
+
+ public GraphicBufferCompat[] newArray(int size) {
+ return new GraphicBufferCompat[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index c18f9b61c044..104a77def44e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -33,6 +33,8 @@ import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
import com.android.systemui.keyguard.KeyguardSliceProvider;
+import java.util.Collections;
+
/**
* View visible under the clock on the lock screen and AoD.
*/
@@ -75,7 +77,8 @@ public class KeyguardSliceView extends LinearLayout {
super.onAttachedToWindow();
// Set initial content
- showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
+ showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
+ Collections.emptyList()));
// Make sure we always have the most current slice
getContext().getContentResolver().registerContentObserver(mKeyguardSliceUri,
@@ -154,7 +157,8 @@ public class KeyguardSliceView extends LinearLayout {
@Override
public void onChange(boolean selfChange, Uri uri) {
- showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
+ showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
+ Collections.emptyList()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 2e4a5a41bfab..22922e7bbac4 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -22,11 +22,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.graphics.Bitmap;
import android.graphics.Rect;
-import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -36,12 +33,14 @@ import android.os.UserHandle;
import android.util.Log;
import android.view.SurfaceControl;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.shared.system.GraphicBufferCompat;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -67,12 +66,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private int mConnectionBackoffAttempts;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
- public Bitmap screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean useIdentityTransform, int rotation) {
+ public GraphicBufferCompat screenshot(Rect sourceCrop, int width, int height, int minLayer,
+ int maxLayer, boolean useIdentityTransform, int rotation) {
long token = Binder.clearCallingIdentity();
try {
- return SurfaceControl.screenshot(sourceCrop, width, height, minLayer, maxLayer,
- useIdentityTransform, rotation);
+ return new GraphicBufferCompat(SurfaceControl.screenshotToBuffer(sourceCrop, width,
+ height, minLayer, maxLayer, useIdentityTransform, rotation));
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index b585bdff292e..f451fda6235e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -37,6 +37,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
@@ -75,17 +76,20 @@ public class NotificationGutsManager implements Dumpable {
private NotificationGuts mNotificationGutsExposed;
private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
private final NotificationInfo.CheckSaveListener mCheckSaveListener;
+ private final OnSettingsClickListener mOnSettingsClickListener;
private String mKeyToRemoveOnGutsClosed;
public NotificationGutsManager(
NotificationPresenter presenter,
NotificationStackScrollLayout stackScroller,
NotificationInfo.CheckSaveListener checkSaveListener,
- Context context) {
+ Context context,
+ OnSettingsClickListener onSettingsClickListener) {
mPresenter = presenter;
mStackScroller = stackScroller;
mCheckSaveListener = checkSaveListener;
mContext = context;
+ mOnSettingsClickListener = onSettingsClickListener;
Resources res = context.getResources();
mNonBlockablePkgs = new HashSet<>();
@@ -189,6 +193,7 @@ public class NotificationGutsManager implements Dumpable {
onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
guts.resetFalsingCheck();
+ mOnSettingsClickListener.onClick(sbn.getKey());
startAppNotificationSettingsActivity(pkg, appUid, channel);
};
}
@@ -352,4 +357,8 @@ public class NotificationGutsManager implements Dumpable {
pw.print("mKeyToRemoveOnGutsClosed: ");
pw.println(mKeyToRemoveOnGutsClosed);
}
+
+ public interface OnSettingsClickListener {
+ void onClick(String key);
+ }
}
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 6775615923d0..1bbb94a6532c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -43,6 +43,7 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
@@ -919,7 +920,14 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
mGutsManager = new NotificationGutsManager(this, mStackScroller,
- mCheckSaveListener, mContext);
+ mCheckSaveListener, mContext,
+ key -> {
+ try {
+ mBarService.onNotificationSettingsViewed(key);
+ } catch (RemoteException e) {
+ // if we're here we're dead
+ }
+ });
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1470,6 +1478,11 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
+ try {
+ mBarService.onNotificationDirectReplied(entry.key);
+ } catch (RemoteException e) {
+ // system process is dead if we're here.
+ }
}
});
@@ -1785,9 +1798,14 @@ public class StatusBar extends SystemUI implements DemoMode,
final int id = n.getId();
final int userId = n.getUserId();
try {
- // TODO: record actual dismissal surface
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (isHeadsUp(n.getKey())) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStackScroller.hasPulsingNotifications()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
- NotificationStats.DISMISSAL_OTHER);
+ dismissalSurface);
if (FORCE_REMOTE_INPUT_HISTORY
&& mKeysKeptForRemoteInput.contains(n.getKey())) {
mKeysKeptForRemoteInput.remove(n.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 87d11b2408b1..4606aee346cc 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -235,7 +235,7 @@ public class UsbPermissionActivity extends AlertActivity
intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
if (mPermissionGranted) {
service.grantDevicePermission(mDevice, mUid);
- if (mAlwaysUse.isChecked()) {
+ if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
final int userId = UserHandle.getUserId(mUid);
service.setDevicePackage(mDevice, mPackageName, userId);
}
@@ -245,7 +245,7 @@ public class UsbPermissionActivity extends AlertActivity
intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
if (mPermissionGranted) {
service.grantAccessoryPermission(mAccessory, mUid);
- if (mAlwaysUse.isChecked()) {
+ if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
final int userId = UserHandle.getUserId(mUid);
service.setAccessoryPackage(mAccessory, mPackageName, userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 4b8f581ac11a..0d41e2029086 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -207,6 +207,7 @@ public class VolumeDialogImpl implements VolumeDialog {
} else {
addExistingRows();
}
+ updateRowsH(getActiveRow());
}
private ColorStateList loadColorStateList(int colorResId) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index af4668a63c03..974148623b00 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1167,7 +1167,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
break;
}
}
+
value = getSanitizedValue(sanitizers, id, value);
+ if (value == null) {
+ if (sDebug) {
+ Slog.d(TAG, "value of required field " + id + " failed sanitization");
+ }
+ allRequiredAreNotEmpty = false;
+ break;
+ }
+ viewState.setSanitizedValue(value);
currentValues.put(id, value);
final AutofillValue filledValue = viewState.getAutofilledValue();
@@ -1337,7 +1346,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return sanitizers;
}
- @NonNull
+ @Nullable
private AutofillValue getSanitizedValue(
@Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
@NonNull AutofillId id,
@@ -1431,10 +1440,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + context);
for (int viewStateNum = 0; viewStateNum < mViewStates.size(); viewStateNum++) {
- final ViewState state = mViewStates.valueAt(viewStateNum);
+ final ViewState viewState = mViewStates.valueAt(viewStateNum);
- final AutofillId id = state.id;
- final AutofillValue value = state.getCurrentValue();
+ final AutofillId id = viewState.id;
+ final AutofillValue value = viewState.getCurrentValue();
if (value == null) {
if (sVerbose) Slog.v(TAG, "callSaveLocked(): skipping " + id);
continue;
@@ -1446,9 +1455,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
- final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+ AutofillValue sanitizedValue = viewState.getSanitizedValue();
- node.updateAutofillValue(sanitizedValue);
+ if (sanitizedValue == null) {
+ // Field is optional and haven't been sanitized yet.
+ sanitizedValue = getSanitizedValue(sanitizers, id, value);
+ }
+ if (sanitizedValue != null) {
+ node.updateAutofillValue(sanitizedValue);
+ } else if (sDebug) {
+ Slog.d(TAG, "Not updating field " + id + " because it failed sanitization");
+ }
}
// Sanitize structure before it's sent to service.
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 832a66b26b12..0dbdc13e429b 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -76,6 +76,7 @@ final class ViewState {
private FillResponse mResponse;
private AutofillValue mCurrentValue;
private AutofillValue mAutofilledValue;
+ private AutofillValue mSanitizedValue;
private Rect mVirtualBounds;
private int mState;
private String mDatasetId;
@@ -117,6 +118,15 @@ final class ViewState {
}
@Nullable
+ AutofillValue getSanitizedValue() {
+ return mSanitizedValue;
+ }
+
+ void setSanitizedValue(@Nullable AutofillValue value) {
+ mSanitizedValue = value;
+ }
+
+ @Nullable
FillResponse getResponse() {
return mResponse;
}
@@ -218,6 +228,7 @@ final class ViewState {
}
pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
+ pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
}
} \ No newline at end of file
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 633bb3e86c26..3d81baf27958 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -43,7 +43,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
android.hardware.oemlock-V1.0-java \
android.hardware.tetheroffload.control-V1.0-java \
android.hardware.vibrator-V1.0-java \
- android.hardware.configstore-V1.0-java
+ android.hardware.configstore-V1.0-java \
+ android.hardware.contexthub-V1.0-java
ifneq ($(INCREMENTAL_BUILDS),)
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
new file mode 100644
index 000000000000..5dd3ee05d66c
--- /dev/null
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -0,0 +1,386 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.PackageOps;
+import android.app.IUidObserver;
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager.ServiceType;
+import android.os.PowerManagerInternal;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Class to track OP_RUN_ANY_IN_BACKGROUND, UID foreground state and "force all app standby".
+ *
+ * TODO Clean up cache when a user is deleted.
+ * TODO Add unit tests. b/68769804.
+ */
+public class ForceAppStandbyTracker {
+ private static final String TAG = "ForceAppStandbyTracker";
+
+ @GuardedBy("ForceAppStandbyTracker.class")
+ private static ForceAppStandbyTracker sInstance;
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+
+ AppOpsManager mAppOpsManager;
+ IAppOpsService mAppOpsService;
+ PowerManagerInternal mPowerManagerInternal;
+
+ private final Handler mCallbackHandler;
+
+ /**
+ * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
+ */
+ @GuardedBy("mLock")
+ final ArraySet<Pair<Integer, String>> mForcedAppStandbyUidPackages = new ArraySet<>();
+
+ @GuardedBy("mLock")
+ final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+
+ @GuardedBy("mLock")
+ final ArraySet<Listener> mListeners = new ArraySet<>();
+
+ @GuardedBy("mLock")
+ boolean mStarted;
+
+ @GuardedBy("mLock")
+ boolean mForceAllAppsStandby;
+
+ public static abstract class Listener {
+ public void onRestrictionChanged(int uid, @Nullable String packageName) {
+ }
+
+ public void onGlobalRestrictionChanged() {
+ }
+ }
+
+ private ForceAppStandbyTracker(Context context) {
+ mContext = context;
+ mCallbackHandler = FgThread.getHandler();
+ }
+
+ /**
+ * Get the singleton instance.
+ */
+ public static synchronized ForceAppStandbyTracker getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new ForceAppStandbyTracker(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Call it when the system is ready.
+ */
+ public void start() {
+ synchronized (mLock) {
+ if (mStarted) {
+ return;
+ }
+ mStarted = true;
+
+ mAppOpsManager = Preconditions.checkNotNull(
+ mContext.getSystemService(AppOpsManager.class));
+ mAppOpsService = Preconditions.checkNotNull(
+ IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE)));
+ mPowerManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(PowerManagerInternal.class));
+
+ try {
+ ActivityManager.getService().registerUidObserver(new UidObserver(),
+ ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
+ | ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
+ new AppOpsWatcher());
+ } catch (RemoteException e) {
+ // shouldn't happen.
+ }
+
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ ServiceType.FORCE_ALL_APPS_STANDBY,
+ state -> updateForceAllAppsStandby(state.batterySaverEnabled));
+
+ updateForceAllAppsStandby(
+ mPowerManagerInternal.getLowPowerState(ServiceType.FORCE_ALL_APPS_STANDBY)
+ .batterySaverEnabled);
+
+ refreshForcedAppStandbyUidPackagesLocked();
+ }
+ }
+
+ /**
+ * Update {@link #mForcedAppStandbyUidPackages} with the current app ops state.
+ */
+ private void refreshForcedAppStandbyUidPackagesLocked() {
+ final int op = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
+
+ mForcedAppStandbyUidPackages.clear();
+ final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(new int[] {op});
+
+ if (ops == null) {
+ return;
+ }
+ final int size = ops.size();
+ for (int i = 0; i < size; i++) {
+ final AppOpsManager.PackageOps pkg = ops.get(i);
+ final List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
+
+ for (int j = 0; j < entries.size(); j++) {
+ AppOpsManager.OpEntry ent = entries.get(j);
+ if (ent.getOp() != op) {
+ continue;
+ }
+ if (ent.getMode() != AppOpsManager.MODE_ALLOWED) {
+ mForcedAppStandbyUidPackages.add(Pair.create(
+ pkg.getUid(), pkg.getPackageName()));
+ }
+ }
+ }
+ }
+
+ boolean isRunAnyInBackgroundAppOpRestricted(int uid, @NonNull String packageName) {
+ try {
+ return mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ uid, packageName) != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false; // shouldn't happen.
+ }
+ }
+
+ private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
+ // TODO Maybe we should switch to indexOf(Pair.create()) if the array size is too big.
+ final int size = mForcedAppStandbyUidPackages.size();
+ for (int i = 0; i < size; i++) {
+ final Pair<Integer, String> pair = mForcedAppStandbyUidPackages.valueAt(i);
+
+ if ((pair.first == uid) && packageName.equals(pair.second)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * @return whether a uid package-name pair is in mForcedAppStandbyUidPackages.
+ */
+ boolean isUidPackageRestrictedLocked(int uid, @NonNull String packageName) {
+ return findForcedAppStandbyUidPackageIndexLocked(uid, packageName) >= 0;
+ }
+
+ boolean updateRestrictedUidPackageLocked(int uid, @NonNull String packageName,
+ boolean restricted) {
+ final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
+ final boolean wasRestricted = index >= 0;
+ if (wasRestricted == restricted) {
+ return false;
+ }
+ if (restricted) {
+ mForcedAppStandbyUidPackages.add(Pair.create(uid, packageName));
+ } else {
+ mForcedAppStandbyUidPackages.removeAt(index);
+ }
+ return true;
+ }
+
+ void uidToForeground(int uid) {
+ synchronized (mLock) {
+ if (!UserHandle.isApp(uid)) {
+ return;
+ }
+ // TODO This can be optimized by calling indexOfKey and sharing the index for get and
+ // put.
+ if (mForegroundUids.get(uid)) {
+ return;
+ }
+ mForegroundUids.put(uid, true);
+ notifyForUidPackage(uid, null);
+ }
+ }
+
+ void uidToBackground(int uid, boolean remove) {
+ synchronized (mLock) {
+ if (!UserHandle.isApp(uid)) {
+ return;
+ }
+ // TODO This can be optimized by calling indexOfKey and sharing the index for get and
+ // put.
+ if (!mForegroundUids.get(uid)) {
+ return;
+ }
+ if (remove) {
+ mForegroundUids.delete(uid);
+ } else {
+ mForegroundUids.put(uid, false);
+ }
+ notifyForUidPackage(uid, null);
+ }
+ }
+
+ // Event handlers
+
+ final class UidObserver extends IUidObserver.Stub {
+ @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ }
+
+ @Override public void onUidGone(int uid, boolean disabled) {
+ uidToBackground(uid, /*remove=*/ true);
+ }
+
+ @Override public void onUidActive(int uid) {
+ uidToForeground(uid);
+ }
+
+ @Override public void onUidIdle(int uid, boolean disabled) {
+ // Just to avoid excessive memcpy, don't remove from the array in this case.
+ uidToBackground(uid, /*remove=*/ false);
+ }
+
+ @Override public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
+ private final class AppOpsWatcher extends IAppOpsCallback.Stub {
+ @Override
+ public void opChanged(int op, int uid, String packageName) throws RemoteException {
+ synchronized (mLock) {
+ final boolean restricted = isRunAnyInBackgroundAppOpRestricted(uid, packageName);
+
+ if (updateRestrictedUidPackageLocked(uid, packageName, restricted)) {
+ notifyForUidPackage(uid, packageName);
+ }
+ }
+ }
+ }
+
+ private Listener[] cloneListeners() {
+ synchronized (mLock) {
+ return mListeners.toArray(new Listener[mListeners.size()]);
+ }
+ }
+
+ void notifyForUidPackage(int uid, String packageName) {
+ mCallbackHandler.post(() -> {
+ for (Listener l : cloneListeners()) {
+ l.onRestrictionChanged(uid, packageName);
+ }
+ });
+ }
+
+ void notifyGlobal() {
+ mCallbackHandler.post(() -> {
+ for (Listener l : cloneListeners()) {
+ l.onGlobalRestrictionChanged();
+ }
+ });
+ }
+
+ void updateForceAllAppsStandby(boolean forceAllAppsStandby) {
+ synchronized (mLock) {
+ if (mForceAllAppsStandby == forceAllAppsStandby) {
+ return;
+ }
+ mForceAllAppsStandby = forceAllAppsStandby;
+ Slog.i(TAG, "Force all app standby: " + mForceAllAppsStandby);
+ notifyGlobal();
+ }
+ }
+
+ // Public interface.
+
+ /**
+ * Register a new listener.
+ */
+ public void addListener(@NonNull Listener listener) {
+ synchronized (mLock) {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Whether force-app-standby is effective for a UID package-name.
+ */
+ public boolean isRestricted(int uid, @NonNull String packageName) {
+ if (isInForeground(uid)) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (mForceAllAppsStandby) {
+ return true;
+ }
+ return isUidPackageRestrictedLocked(uid, packageName);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isInForeground(int uid) {
+ if (!UserHandle.isApp(uid)) {
+ return true;
+ }
+ synchronized (mLock) {
+ return mForegroundUids.get(uid);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isForceAllAppsStandbyEnabled() {
+ synchronized (mLock) {
+ return mForceAllAppsStandby;
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isRunAnyInBackgroundAppOpsAllowed(int uid, @NonNull String packageName) {
+ synchronized (mLock) {
+ return !isUidPackageRestrictedLocked(uid, packageName);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public SparseBooleanArray getForegroudUids() {
+ synchronized (mLock) {
+ return mForegroundUids.clone();
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public ArraySet<Pair<Integer, String>> getRestrictedUidPackages() {
+ synchronized (mLock) {
+ return new ArraySet(mForcedAppStandbyUidPackages);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2131731de045..3cd2f6ae3b31 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -58,6 +58,7 @@ import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityManagerService.NeededUriGrants;
+import com.android.server.am.proto.ActiveServicesProto;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -85,6 +86,7 @@ import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.webkit.WebViewZygote;
public final class ActiveServices {
@@ -633,7 +635,7 @@ public final class ActiveServices {
sb.append("Stopping service due to app idle: ");
UserHandle.formatUid(sb, service.appInfo.uid);
sb.append(" ");
- TimeUtils.formatDuration(service.createTime
+ TimeUtils.formatDuration(service.createRealTime
- SystemClock.elapsedRealtime(), sb);
sb.append(" ");
sb.append(compName);
@@ -1043,8 +1045,8 @@ public final class ActiveServices {
try {
if (AppGlobals.getPackageManager().checkPermission(
android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
- r.appInfo.packageName,
- r.appInfo.uid) != PackageManager.PERMISSION_GRANTED) {
+ r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid))
+ != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Instant app " + r.appInfo.packageName
+ " does not have permission to create foreground"
+ "services");
@@ -3220,7 +3222,7 @@ public final class ActiveServices {
info.uid = r.appInfo.uid;
info.process = r.processName;
info.foreground = r.isForeground;
- info.activeSince = r.createTime;
+ info.activeSince = r.createRealTime;
info.started = r.startRequested;
info.clientCount = r.connections.size();
info.crashCount = r.crashCount;
@@ -3574,7 +3576,7 @@ public final class ActiveServices {
pw.print(" app=");
pw.println(r.app);
pw.print(" created=");
- TimeUtils.formatDuration(r.createTime, nowReal, pw);
+ TimeUtils.formatDuration(r.createRealTime, nowReal, pw);
pw.print(" started=");
pw.print(r.startRequested);
pw.print(" connections=");
@@ -3840,6 +3842,26 @@ public final class ActiveServices {
return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
}
+ protected void writeToProto(ProtoOutputStream proto) {
+ synchronized (mAm) {
+ int[] users = mAm.mUserController.getUsers();
+ for (int user : users) {
+ ServiceMap smap = mServiceMap.get(user);
+ if (smap == null) {
+ continue;
+ }
+ long token = proto.start(ActiveServicesProto.SERVICES_BY_USERS);
+ proto.write(ActiveServicesProto.ServicesByUser.USER_ID, user);
+ ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+ for (int i=0; i<alls.size(); i++) {
+ alls.valueAt(i).writeToProto(proto,
+ ActiveServicesProto.ServicesByUser.SERVICE_RECORDS);
+ }
+ proto.end(token);
+ }
+ }
+ }
+
/**
* There are three ways to call this:
* - no service specified: dump all the services
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 43618564652c..b3b831edcab7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -415,6 +415,8 @@ import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.proto.ActivityManagerServiceProto;
import com.android.server.am.proto.BroadcastProto;
+import com.android.server.am.proto.GrantUriProto;
+import com.android.server.am.proto.NeededUriGrantsProto;
import com.android.server.am.proto.StickyBroadcastProto;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
@@ -1191,6 +1193,13 @@ public class ActivityManagerService extends IActivityManager.Stub
return result;
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(GrantUriProto.URI, uri.toString());
+ proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
+ proto.end(token);
+ }
+
public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
ContentProvider.getUriWithoutUserId(uri), false);
@@ -9044,6 +9053,19 @@ public class ActivityManagerService extends IActivityManager.Stub
this.targetUid = targetUid;
this.flags = flags;
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
+ proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
+ proto.write(NeededUriGrantsProto.FLAGS, flags);
+
+ final int N = this.size();
+ for (int i=0; i<N; i++) {
+ this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
+ }
+ proto.end(token);
+ }
}
/**
@@ -14922,6 +14944,25 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
writeBroadcastsToProtoLocked(proto);
}
+ } else if ("provider".equals(cmd)) {
+ String[] newArgs;
+ String name;
+ if (opti >= args.length) {
+ name = null;
+ newArgs = EMPTY_STRING_ARRAY;
+ } else {
+ name = args[opti];
+ opti++;
+ newArgs = new String[args.length - opti];
+ if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
+ args.length - opti);
+ }
+ if (!dumpProviderProto(fd, pw, name, newArgs)) {
+ pw.println("No providers match: " + name);
+ pw.println("Use -h for help.");
+ }
+ } else if ("service".equals(cmd)) {
+ mServices.writeToProto(proto);
} else {
// default option, dump everything, output is ActivityManagerServiceProto
synchronized (this) {
@@ -14932,6 +14973,10 @@ public class ActivityManagerService extends IActivityManager.Stub
long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
writeBroadcastsToProtoLocked(proto);
proto.end(broadcastToken);
+
+ long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
+ mServices.writeToProto(proto);
+ proto.end(serviceToken);
}
}
proto.flush();
@@ -16063,6 +16108,15 @@ public class ActivityManagerService extends IActivityManager.Stub
return mProviderMap.dumpProvider(fd, pw, name, args, opti, dumpAll);
}
+ /**
+ * Similar to the dumpProvider, but only dumps the first matching provider.
+ * The provider is responsible for dumping as proto.
+ */
+ protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
+ String[] args) {
+ return mProviderMap.dumpProviderProto(fd, pw, name, args);
+ }
+
static class ItemMatcher {
ArrayList<ComponentName> components;
ArrayList<String> strings;
diff --git a/services/core/java/com/android/server/am/AppBindRecord.java b/services/core/java/com/android/server/am/AppBindRecord.java
index df833ad86c35..7b3859789d28 100644
--- a/services/core/java/com/android/server/am/AppBindRecord.java
+++ b/services/core/java/com/android/server/am/AppBindRecord.java
@@ -17,6 +17,9 @@
package com.android.server.am;
import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
import java.io.PrintWriter;
@@ -60,4 +63,18 @@ final class AppBindRecord {
+ Integer.toHexString(System.identityHashCode(this))
+ " " + service.shortName + ":" + client.processName + "}";
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(AppBindRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ if (client != null) {
+ client.writeToProto(proto, AppBindRecordProto.CLIENT);
+ }
+ final int N = connections.size();
+ for (int i=0; i<N; i++) {
+ connections.valueAt(i).writeToProto(proto, AppBindRecordProto.CONNECTIONS);
+ }
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 9b7a0c43b191..6df283ca0a77 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -19,6 +19,9 @@ package com.android.server.am;
import android.app.IServiceConnection;
import android.app.PendingIntent;
import android.content.Context;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.ConnectionRecordProto;
import java.io.PrintWriter;
@@ -119,4 +122,70 @@ final class ConnectionRecord {
sb.append('}');
return stringName = sb.toString();
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ if (binding == null) return; // if binding is null, don't write data, something is wrong.
+ long token = proto.start(fieldId);
+ proto.write(ConnectionRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ if (binding.client != null) {
+ proto.write(ConnectionRecordProto.USER_ID, binding.client.userId);
+ }
+ if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.AUTO_CREATE);
+ }
+ if ((flags&Context.BIND_DEBUG_UNBIND) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEBUG_UNBIND);
+ }
+ if ((flags&Context.BIND_NOT_FOREGROUND) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_FG);
+ }
+ if ((flags&Context.BIND_IMPORTANT_BACKGROUND) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT_BG);
+ }
+ if ((flags&Context.BIND_ABOVE_CLIENT) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ABOVE_CLIENT);
+ }
+ if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ALLOW_OOM_MANAGEMENT);
+ }
+ if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.WAIVE_PRIORITY);
+ }
+ if ((flags&Context.BIND_IMPORTANT) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT);
+ }
+ if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ADJUST_WITH_ACTIVITY);
+ }
+ if ((flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE_WHILE_WAKE);
+ }
+ if ((flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE);
+ }
+ if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.TREAT_LIKE_ACTIVITY);
+ }
+ if ((flags&Context.BIND_VISIBLE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.VISIBLE);
+ }
+ if ((flags&Context.BIND_SHOWING_UI) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.SHOWING_UI);
+ }
+ if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_VISIBLE);
+ }
+ if (serviceDead) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEAD);
+ }
+ if (binding.service != null) {
+ proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortName);
+ }
+ if (conn != null) {
+ proto.write(ConnectionRecordProto.CONN_HEX_HASH,
+ Integer.toHexString(System.identityHashCode(conn.asBinder())));
+ }
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index be290e953960..01ce64c6e037 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -21,6 +21,10 @@ import android.content.Intent;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
+import com.android.server.am.proto.IntentBindRecordProto;
import java.io.PrintWriter;
@@ -106,4 +110,32 @@ final class IntentBindRecord {
sb.append('}');
return stringName = sb.toString();
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(IntentBindRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ proto.write(IntentBindRecordProto.IS_CREATE,
+ (collectFlags()&Context.BIND_AUTO_CREATE) != 0);
+ if (intent != null) {
+ intent.getIntent().writeToProto(proto,
+ IntentBindRecordProto.INTENT, false, true, false, false);
+ }
+ if (binder != null) {
+ proto.write(IntentBindRecordProto.BINDER, binder.toString());
+ }
+ proto.write(IntentBindRecordProto.REQUESTED, requested);
+ proto.write(IntentBindRecordProto.RECEIVED, received);
+ proto.write(IntentBindRecordProto.HAS_BOUND, hasBound);
+ proto.write(IntentBindRecordProto.DO_REBIND, doRebind);
+
+ final int N = apps.size();
+ for (int i=0; i<N; i++) {
+ AppBindRecord a = apps.valueAt(i);
+ if (a != null) {
+ a.writeToProto(proto, IntentBindRecordProto.APPS);
+ }
+ }
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 32d03daedef3..8a905f8c045e 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -28,6 +28,7 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -322,8 +323,7 @@ public final class ProviderMap {
return needSep;
}
- protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
- int opti, boolean dumpAll) {
+ private ArrayList<ContentProviderRecord> getProvidersForName(String name) {
ArrayList<ContentProviderRecord> allProviders = new ArrayList<ContentProviderRecord>();
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
@@ -365,6 +365,12 @@ public final class ProviderMap {
}
}
}
+ return providers;
+ }
+
+ protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll) {
+ ArrayList<ContentProviderRecord> providers = getProvidersForName(name);
if (providers.size() <= 0) {
return false;
@@ -417,6 +423,33 @@ public final class ProviderMap {
}
/**
+ * Similar to the dumpProvider, but only dumps the first matching provider.
+ * The provider is responsible for dumping as proto.
+ */
+ protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
+ String[] args) {
+ //add back the --proto arg, which was stripped out by PriorityDump
+ String[] newArgs = Arrays.copyOf(args, args.length + 1);
+ newArgs[args.length] = "--proto";
+
+ ArrayList<ContentProviderRecord> providers = getProvidersForName(name);
+
+ if (providers.size() <= 0) {
+ return false;
+ }
+
+ // Only dump the first provider, since we are dumping in proto format
+ for (int i = 0; i < providers.size(); i++) {
+ final ContentProviderRecord r = providers.get(i);
+ if (r.proc != null && r.proc.thread != null) {
+ dumpToTransferPipe(null, fd, pw, r, newArgs);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Invokes IApplicationThread.dumpProvider() on the thread of the specified provider without
* any meta string (e.g., provider info, indentation) written to the file descriptor.
*/
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 16995e50fdbf..b6eff003d26e 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.LocalServices;
+import com.android.server.am.proto.ServiceRecordProto;
import com.android.server.notification.NotificationManagerInternal;
import android.app.INotificationManager;
@@ -42,6 +43,8 @@ import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -79,7 +82,7 @@ final class ServiceRecord extends Binder {
final String permission;// permission needed to access service
final boolean exported; // from ServiceInfo.exported
final Runnable restarter; // used to schedule retries of starting the service
- final long createTime; // when this service was created
+ final long createRealTime; // when this service was created
final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
= new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
// All active bindings to the service.
@@ -103,7 +106,7 @@ final class ServiceRecord extends Binder {
boolean startRequested; // someone explicitly called start?
boolean delayedStop; // service has been stopped but is in a delayed start?
boolean stopIfKilled; // last onStart() said to stop if service killed?
- boolean callStart; // last onStart() has asked to alway be called on restart.
+ boolean callStart; // last onStart() has asked to always be called on restart.
int executeNesting; // number of outstanding operations keeping foreground.
boolean executeFg; // should we be executing in the foreground?
long executingStart; // start time of last execute request.
@@ -159,6 +162,27 @@ final class ServiceRecord extends Binder {
}
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
+ long token = proto.start(fieldId);
+ proto.write(ServiceRecordProto.StartItemProto.ID, id);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.StartItemProto.DURATION, deliveredTime, now);
+ proto.write(ServiceRecordProto.StartItemProto.DELIVERY_COUNT, deliveryCount);
+ proto.write(ServiceRecordProto.StartItemProto.DONE_EXECUTING_COUNT, doneExecutingCount);
+ if (intent != null) {
+ intent.writeToProto(proto, ServiceRecordProto.StartItemProto.INTENT, true, true,
+ true, false);
+ }
+ if (neededGrants != null) {
+ neededGrants.writeToProto(proto, ServiceRecordProto.StartItemProto.NEEDED_GRANTS);
+ }
+ if (uriPermissions != null) {
+ uriPermissions.writeToProto(proto,
+ ServiceRecordProto.StartItemProto.URI_PERMISSIONS);
+ }
+ proto.end(token);
+ }
+
public String toString() {
if (stringName != null) {
return stringName;
@@ -209,6 +233,117 @@ final class ServiceRecord extends Binder {
}
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(ServiceRecordProto.SHORT_NAME, this.shortName);
+ proto.write(ServiceRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ proto.write(ServiceRecordProto.IS_RUNNING, app != null);
+ if (app != null) {
+ proto.write(ServiceRecordProto.PID, app.pid);
+ }
+ if (intent != null) {
+ intent.getIntent().writeToProto(proto, ServiceRecordProto.INTENT, false, true, false,
+ true);
+ }
+ proto.write(ServiceRecordProto.PACKAGE_NAME, packageName);
+ proto.write(ServiceRecordProto.PROCESS_NAME, processName);
+ proto.write(ServiceRecordProto.PERMISSION, permission);
+
+ long now = SystemClock.uptimeMillis();
+ long nowReal = SystemClock.elapsedRealtime();
+ if (appInfo != null) {
+ long appInfoToken = proto.start(ServiceRecordProto.APPINFO);
+ proto.write(ServiceRecordProto.AppInfo.BASE_DIR, appInfo.sourceDir);
+ if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+ proto.write(ServiceRecordProto.AppInfo.RES_DIR, appInfo.publicSourceDir);
+ }
+ proto.write(ServiceRecordProto.AppInfo.DATA_DIR, appInfo.dataDir);
+ proto.end(appInfoToken);
+ }
+ if (app != null) {
+ app.writeToProto(proto, ServiceRecordProto.APP);
+ }
+ if (isolatedProc != null) {
+ isolatedProc.writeToProto(proto, ServiceRecordProto.ISOLATED_PROC);
+ }
+ proto.write(ServiceRecordProto.WHITELIST_MANAGER, whitelistManager);
+ proto.write(ServiceRecordProto.DELAYED, delayed);
+ if (isForeground || foregroundId != 0) {
+ long fgToken = proto.start(ServiceRecordProto.FOREGROUND);
+ proto.write(ServiceRecordProto.Foreground.ID, foregroundId);
+ foregroundNoti.writeToProto(proto, ServiceRecordProto.Foreground.NOTIFICATION);
+ proto.end(fgToken);
+ }
+ ProtoUtils.toDuration(proto, ServiceRecordProto.CREATE_REAL_TIME, createRealTime, nowReal);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.STARTING_BG_TIMEOUT, startingBgTimeout, now);
+ ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now);
+ ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
+ proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
+
+ if (startRequested || delayedStop || lastStartId != 0) {
+ long startToken = proto.start(ServiceRecordProto.START);
+ proto.write(ServiceRecordProto.Start.START_REQUESTED, startRequested);
+ proto.write(ServiceRecordProto.Start.DELAYED_STOP, delayedStop);
+ proto.write(ServiceRecordProto.Start.STOP_IF_KILLED, stopIfKilled);
+ proto.write(ServiceRecordProto.Start.LAST_START_ID, lastStartId);
+ proto.end(startToken);
+ }
+
+ if (executeNesting != 0) {
+ long executNestingToken = proto.start(ServiceRecordProto.EXECUTE);
+ proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_NESTING, executeNesting);
+ proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_FG, executeFg);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.ExecuteNesting.EXECUTING_START, executingStart, now);
+ proto.end(executNestingToken);
+ }
+ if (destroying || destroyTime != 0) {
+ ProtoUtils.toDuration(proto, ServiceRecordProto.DESTORY_TIME, destroyTime, now);
+ }
+ if (crashCount != 0 || restartCount != 0 || restartDelay != 0 || nextRestartTime != 0) {
+ long crashToken = proto.start(ServiceRecordProto.CRASH);
+ proto.write(ServiceRecordProto.Crash.RESTART_COUNT, restartCount);
+ ProtoUtils.toDuration(proto, ServiceRecordProto.Crash.RESTART_DELAY, restartDelay, now);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.Crash.NEXT_RESTART_TIME, nextRestartTime, now);
+ proto.write(ServiceRecordProto.Crash.CRASH_COUNT, crashCount);
+ proto.end(crashToken);
+ }
+
+ if (deliveredStarts.size() > 0) {
+ final int N = deliveredStarts.size();
+ for (int i = 0; i < N; i++) {
+ deliveredStarts.get(i).writeToProto(proto,
+ ServiceRecordProto.DELIVERED_STARTS, now);
+ }
+ }
+ if (pendingStarts.size() > 0) {
+ final int N = pendingStarts.size();
+ for (int i = 0; i < N; i++) {
+ pendingStarts.get(i).writeToProto(proto, ServiceRecordProto.PENDING_STARTS, now);
+ }
+ }
+ if (bindings.size() > 0) {
+ final int N = bindings.size();
+ for (int i=0; i<N; i++) {
+ IntentBindRecord b = bindings.valueAt(i);
+ b.writeToProto(proto, ServiceRecordProto.BINDINGS);
+ }
+ }
+ if (connections.size() > 0) {
+ final int N = connections.size();
+ for (int conni=0; conni<N; conni++) {
+ ArrayList<ConnectionRecord> c = connections.valueAt(conni);
+ for (int i=0; i<c.size(); i++) {
+ c.get(i).writeToProto(proto, ServiceRecordProto.CONNECTIONS);
+ }
+ }
+ }
+ proto.end(token);
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("intent={");
pw.print(intent.getIntent().toShortString(false, true, false, true));
@@ -243,7 +378,7 @@ final class ServiceRecord extends Binder {
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
pw.print(prefix); pw.print("createTime=");
- TimeUtils.formatDuration(createTime, nowReal, pw);
+ TimeUtils.formatDuration(createRealTime, nowReal, pw);
pw.print(" startingBgTimeout=");
TimeUtils.formatDuration(startingBgTimeout, now, pw);
pw.println();
@@ -329,7 +464,7 @@ final class ServiceRecord extends Binder {
permission = sInfo.permission;
exported = sInfo.exported;
this.restarter = restarter;
- createTime = SystemClock.elapsedRealtime();
+ createRealTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
userId = UserHandle.getUserId(appInfo.uid);
createdFromFg = callerIsFg;
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
index 28344df285f2..fc07c1ab8c9d 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/am/UriPermissionOwner.java
@@ -20,6 +20,9 @@ import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.UriPermissionOwnerProto;
import com.google.android.collect.Sets;
@@ -139,6 +142,26 @@ final class UriPermissionOwner {
}
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(UriPermissionOwnerProto.OWNER, owner.toString());
+ if (mReadPerms != null) {
+ synchronized (mReadPerms) {
+ for (UriPermission p : mReadPerms) {
+ p.uri.writeToProto(proto, UriPermissionOwnerProto.READ_PERMS);
+ }
+ }
+ }
+ if (mWritePerms != null) {
+ synchronized (mWritePerms) {
+ for (UriPermission p : mWritePerms) {
+ p.uri.writeToProto(proto, UriPermissionOwnerProto.WRITE_PERMS);
+ }
+ }
+ }
+ proto.end(token);
+ }
+
@Override
public String toString() {
return owner.toString();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2df5dc93768c..4e3d8d27e0f9 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -89,6 +89,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.pm.UserManagerService;
@@ -355,27 +356,35 @@ class UserController implements Handler.Callback {
// Only keep marching forward if user is actually unlocked
if (!StorageManager.isUserKeyUnlocked(userId)) return;
synchronized (mLock) {
- // Bail if we ended up with a stale user
- if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
-
- // Do not proceed if unexpected state
- if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
+ // Do not proceed if unexpected state or a stale user
+ if (mStartedUsers.get(userId) != uss || uss.state != STATE_RUNNING_LOCKED) {
return;
}
}
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
uss.mUnlockProgress.start();
// Prepare app storage before we go any further
uss.mUnlockProgress.setProgress(5,
mInjector.getContext().getString(R.string.android_start_title));
- mInjector.getUserManager().onBeforeUnlockUser(userId);
- uss.mUnlockProgress.setProgress(20);
- // Dispatch unlocked to system services; when fully dispatched,
- // that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
+ // Call onBeforeUnlockUser on a worker thread that allows disk I/O
+ FgThread.getHandler().post(() -> {
+ mInjector.getUserManager().onBeforeUnlockUser(userId);
+ synchronized (mLock) {
+ // Do not proceed if unexpected state
+ if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
+ return;
+ }
+ }
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+
+ uss.mUnlockProgress.setProgress(20);
+
+ // Dispatch unlocked to system services; when fully dispatched,
+ // that calls through to the next "unlocked" phase
+ mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
+ .sendToTarget();
+ });
}
/**
@@ -1819,7 +1828,10 @@ class UserController implements Handler.Callback {
case SYSTEM_USER_UNLOCK_MSG:
final int userId = msg.arg1;
mInjector.getSystemServiceManager().unlockUser(userId);
- mInjector.loadUserRecents(userId);
+ // Loads recents on a worker thread that allows disk I/O
+ FgThread.getHandler().post(() -> {
+ mInjector.loadUserRecents(userId);
+ });
if (userId == UserHandle.USER_SYSTEM) {
mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
}
diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/Tuner.java
index e6ae320cf38d..2ea4271864f2 100644
--- a/services/core/java/com/android/server/broadcastradio/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/Tuner.java
@@ -27,8 +27,10 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
class Tuner extends ITuner.Stub {
private static final String TAG = "BroadcastRadioService.Tuner";
@@ -96,6 +98,10 @@ class Tuner extends ITuner.Stub {
private native boolean nativeIsAnalogForced(long nativeContext);
private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
+ private native Map<String, String> nativeSetParameters(long nativeContext,
+ Map<String, String> parameters);
+ private native Map<String, String> nativeGetParameters(long nativeContext, List<String> keys);
+
private native boolean nativeIsAntennaConnected(long nativeContext);
@Override
@@ -273,6 +279,31 @@ class Tuner extends ITuner.Stub {
}
@Override
+ public Map setParameters(Map parameters) {
+ Map<String, String> results;
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ results = nativeSetParameters(mNativeContext, Objects.requireNonNull(parameters));
+ }
+ if (results == null) return Collections.emptyMap();
+ return results;
+ }
+
+ @Override
+ public Map getParameters(List<String> keys) {
+ if (keys == null) {
+ throw new IllegalArgumentException("The argument must not be a null pointer");
+ }
+ Map<String, String> results;
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ results = nativeGetParameters(mNativeContext, keys);
+ }
+ if (results == null) return Collections.emptyMap();
+ return results;
+ }
+
+ @Override
public boolean isAntennaConnected() {
synchronized (mLock) {
checkNotClosedLocked();
diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
index a87ae8d65bf8..2460c67a64a2 100644
--- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
@@ -26,6 +26,9 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import java.util.List;
+import java.util.Map;
+
class TunerCallback implements ITunerCallback {
private static final String TAG = "BroadcastRadioService.TunerCallback";
@@ -121,6 +124,11 @@ class TunerCallback implements ITunerCallback {
}
@Override
+ public void onParametersUpdated(Map parameters) {
+ dispatch(() -> mClientCallback.onParametersUpdated(parameters));
+ }
+
+ @Override
public IBinder asBinder() {
throw new RuntimeException("Not a binder");
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 4bdbbe3959b7..e243e56cbd7e 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -21,6 +21,7 @@ import static android.util.TimeUtils.NANOS_PER_MS;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetdEventCallback;
+import android.net.MacAddress;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.metrics.ConnectStats;
@@ -35,6 +36,7 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.util.ArrayMap;
import android.util.SparseArray;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -242,13 +244,17 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
event.timestampMs = timestampMs;
event.uid = uid;
event.ethertype = ethertype;
- event.dstHwAddr = dstHw;
+ event.dstHwAddr = new MacAddress(dstHw);
event.srcIp = srcIp;
event.dstIp = dstIp;
event.ipNextHeader = ipNextHeader;
event.srcPort = srcPort;
event.dstPort = dstPort;
addWakeupEvent(event);
+
+ String dstMac = event.dstHwAddr.toString();
+ StatsLog.write(StatsLog.PACKET_WAKEUP_OCCURRED,
+ uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
}
private void addWakeupEvent(WakeupEvent event) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b5497681a2de..b9777ec0d968 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -622,24 +622,15 @@ public final class JobSchedulerService extends com.android.server.SystemService
if (disabled) {
cancelJobsForUid(uid, "uid gone");
}
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, false);
- }
}
@Override public void onUidActive(int uid) throws RemoteException {
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, true);
- }
}
@Override public void onUidIdle(int uid, boolean disabled) {
if (disabled) {
cancelJobsForUid(uid, "app uid idle");
}
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, false);
- }
}
@Override public void onUidCachedChanged(int uid, boolean cached) {
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 78b4160e8614..f67bd0469b24 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -25,19 +24,20 @@ import android.os.IDeviceIdleController;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
+import com.android.server.ForceAppStandbyTracker;
+import com.android.server.ForceAppStandbyTracker.Listener;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
import java.io.PrintWriter;
+import java.util.function.Predicate;
public final class BackgroundJobsController extends StateController {
@@ -49,18 +49,13 @@ public final class BackgroundJobsController extends StateController {
private static volatile BackgroundJobsController sController;
private final JobSchedulerService mJobSchedulerService;
- private final IAppOpsService mAppOpsService;
private final IDeviceIdleController mDeviceIdleController;
- private final SparseBooleanArray mForegroundUids;
private int[] mPowerWhitelistedUserAppIds;
private int[] mTempWhitelistedAppIds;
- /**
- * Only tracks jobs for which source package app op RUN_ANY_IN_BACKGROUND is not ALLOWED.
- * Maps jobs to the sourceUid unlike the global {@link JobSchedulerService#mJobs JobStore}
- * which uses callingUid.
- */
- private SparseArray<ArraySet<JobStatus>> mTrackedJobs;
+
+ private final ForceAppStandbyTracker mForceAppStandbyTracker;
+
public static BackgroundJobsController get(JobSchedulerService service) {
synchronized (sCreationLock) {
@@ -89,9 +84,7 @@ public final class BackgroundJobsController extends StateController {
} catch (RemoteException rexc) {
Slog.e(LOG_TAG, "Device idle controller not reachable");
}
- if (checkAllTrackedJobsLocked()) {
- mStateChangedListener.onControllerStateChanged();
- }
+ updateAllJobRestrictionsLocked();
}
}
};
@@ -99,16 +92,12 @@ public final class BackgroundJobsController extends StateController {
private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
super(service, context, lock);
mJobSchedulerService = service;
- mAppOpsService = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mForegroundUids = new SparseBooleanArray();
- mTrackedJobs = new SparseArray<>();
+ mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
+
try {
- mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
- new AppOpsWatcher());
mPowerWhitelistedUserAppIds = mDeviceIdleController.getAppIdUserWhitelist();
mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
} catch (RemoteException rexc) {
@@ -120,184 +109,165 @@ public final class BackgroundJobsController extends StateController {
powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
context.registerReceiverAsUser(mDozeWhitelistReceiver, UserHandle.ALL, powerWhitelistFilter,
null, null);
+
+ mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
+ mForceAppStandbyTracker.start();
}
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
- final int uid = jobStatus.getSourceUid();
- final String packageName = jobStatus.getSourcePackageName();
- try {
- final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
- uid, packageName);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
- return;
- }
- } catch (RemoteException rexc) {
- Slog.e(LOG_TAG, "Cannot reach app ops service", rexc);
- }
- jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRunJobLocked(uid));
- startTrackingJobLocked(jobStatus);
+ updateSingleJobRestrictionLocked(jobStatus);
}
@Override
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
- stopTrackingJobLocked(jobStatus);
- }
-
- /* Called by JobSchedulerService to report uid state changes between active and idle */
- public void setUidActiveLocked(int uid, boolean active) {
- final boolean changed = (active != mForegroundUids.get(uid));
- if (!changed) {
- return;
- }
- if (DEBUG) {
- Slog.d(LOG_TAG, "uid " + uid + " going to " + (active ? "fg" : "bg"));
- }
- if (active) {
- mForegroundUids.put(uid, true);
- } else {
- mForegroundUids.delete(uid);
- }
- if (checkTrackedJobsForUidLocked(uid)) {
- mStateChangedListener.onControllerStateChanged();
- }
}
@Override
public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
pw.println("BackgroundJobsController");
+
+ pw.print("Force all apps standby: ");
+ pw.println(mForceAppStandbyTracker.isForceAllAppsStandbyEnabled());
+
pw.print("Foreground uids: [");
- for (int i = 0; i < mForegroundUids.size(); i++) {
- if (mForegroundUids.valueAt(i)) pw.print(mForegroundUids.keyAt(i) + " ");
+ final SparseBooleanArray foregroundUids = mForceAppStandbyTracker.getForegroudUids();
+
+ String sep = "";
+ for (int i = 0; i < foregroundUids.size(); i++) {
+ if (foregroundUids.valueAt(i)) {
+ pw.print(sep);
+ pw.print(UserHandle.formatUid(foregroundUids.keyAt(i)));
+ sep = " ";
+ }
}
pw.println("]");
- mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
- @Override
- public void process(JobStatus jobStatus) {
- if (!jobStatus.shouldDump(filterUid)) {
- return;
- }
- final int uid = jobStatus.getSourceUid();
- pw.print(" #");
- jobStatus.printUniqueId(pw);
- pw.print(" from ");
- UserHandle.formatUid(pw, uid);
- pw.print(mForegroundUids.get(uid) ? " foreground" : " background");
- if (isWhitelistedLocked(uid)) {
- pw.print(", whitelisted");
- }
- pw.print(": ");
- pw.print(jobStatus.getSourcePackageName());
- pw.print(" [background restrictions");
- final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- pw.print(jobsForUid != null && jobsForUid.contains(jobStatus) ? " on]" : " off]");
- if ((jobStatus.satisfiedConstraints
- & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
- pw.println(" RUNNABLE");
- } else {
- pw.println(" WAITING");
- }
+
+ pw.println("Restricted packages:");
+ for (Pair<Integer, String> uidAndPackage
+ : mForceAppStandbyTracker.getRestrictedUidPackages()) {
+ pw.print(" ");
+ pw.print(UserHandle.formatUid(uidAndPackage.first));
+ pw.print(" ");
+ pw.print(uidAndPackage.second);
+ pw.println();
+ }
+
+ pw.println("Job state:");
+ mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
+ if (!jobStatus.shouldDump(filterUid)) {
+ return;
+ }
+ final int uid = jobStatus.getSourceUid();
+ pw.print(" #");
+ jobStatus.printUniqueId(pw);
+ pw.print(" from ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(mForceAppStandbyTracker.isInForeground(uid) ? " foreground" : " background");
+ if (isWhitelistedLocked(uid)) {
+ pw.print(", whitelisted");
+ }
+ pw.print(": ");
+ pw.print(jobStatus.getSourcePackageName());
+
+ pw.print(" [background restrictions ");
+ pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
+ jobStatus.getSourceUid(), jobStatus.getSourcePackageName()) ? "off]" : "on]");
+
+ if ((jobStatus.satisfiedConstraints
+ & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+ pw.println(" RUNNABLE");
+ } else {
+ pw.println(" WAITING");
}
});
}
- void startTrackingJobLocked(JobStatus jobStatus) {
- final int uid = jobStatus.getSourceUid();
- ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- if (jobsForUid == null) {
- jobsForUid = new ArraySet<>();
- mTrackedJobs.put(uid, jobsForUid);
- }
- jobsForUid.add(jobStatus);
+ private void updateAllJobRestrictionsLocked() {
+ updateJobRestrictionsLocked(/*filterUid=*/ -1);
}
- void stopTrackingJobLocked(JobStatus jobStatus) {
- final int uid = jobStatus.getSourceUid();
- ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- if (jobsForUid != null) {
- jobsForUid.remove(jobStatus);
- }
+ private void updateJobRestrictionsForUidLocked(int uid) {
+
+ // TODO Use forEachJobForSourceUid() once we have it.
+
+ updateJobRestrictionsLocked(/*filterUid=*/ uid);
}
- boolean checkAllTrackedJobsLocked() {
- boolean changed = false;
- for (int i = 0; i < mTrackedJobs.size(); i++) {
- changed |= checkTrackedJobsForUidLocked(mTrackedJobs.keyAt(i));
+ private void updateJobRestrictionsLocked(int filterUid) {
+ final UpdateJobFunctor updateTrackedJobs =
+ new UpdateJobFunctor(filterUid);
+
+ final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0;
+
+ mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
+
+ final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0;
+ if (DEBUG) {
+ Slog.d(LOG_TAG, String.format(
+ "Job status updated: %d/%d checked/total jobs, %d us",
+ updateTrackedJobs.mCheckedCount,
+ updateTrackedJobs.mTotalCount,
+ (time / 1000)
+ ));
}
- return changed;
- }
- private boolean checkTrackedJobsForUidLocked(int uid) {
- final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- boolean changed = false;
- if (jobsForUid != null) {
- for (int i = 0; i < jobsForUid.size(); i++) {
- JobStatus jobStatus = jobsForUid.valueAt(i);
- changed |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
- canRunJobLocked(uid));
- }
+ if (updateTrackedJobs.mChanged) {
+ mStateChangedListener.onControllerStateChanged();
}
- return changed;
}
- boolean isWhitelistedLocked(int uid) {
- return ArrayUtils.contains(mTempWhitelistedAppIds, UserHandle.getAppId(uid))
- || ArrayUtils.contains(mPowerWhitelistedUserAppIds, UserHandle.getAppId(uid));
+ private boolean isWhitelistedLocked(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ return ArrayUtils.contains(mTempWhitelistedAppIds, appId)
+ || ArrayUtils.contains(mPowerWhitelistedUserAppIds, appId);
}
- boolean canRunJobLocked(int uid) {
- return mForegroundUids.get(uid) || isWhitelistedLocked(uid);
- }
+ boolean updateSingleJobRestrictionLocked(JobStatus jobStatus) {
- private final class AppOpsWatcher extends IAppOpsCallback.Stub {
- @Override
- public void opChanged(int op, int uid, String packageName) throws RemoteException {
- synchronized (mLock) {
- final int mode = mAppOpsService.checkOperation(op, uid, packageName);
- if (DEBUG) {
- Slog.d(LOG_TAG,
- "Appop changed for " + uid + ", " + packageName + " to " + mode);
- }
- final boolean shouldTrack = (mode != AppOpsManager.MODE_ALLOWED);
- UpdateTrackedJobsFunc updateTrackedJobs = new UpdateTrackedJobsFunc(uid,
- packageName, shouldTrack);
- mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
- if (updateTrackedJobs.mChanged) {
- mStateChangedListener.onControllerStateChanged();
- }
- }
- }
+ final int uid = jobStatus.getSourceUid();
+ final String packageName = jobStatus.getSourcePackageName();
+
+ final boolean canRun = isWhitelistedLocked(uid)
+ || !mForceAppStandbyTracker.isRestricted(uid, packageName);
+
+ return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
}
- private final class UpdateTrackedJobsFunc implements JobStore.JobStatusFunctor {
- private final String mPackageName;
- private final int mUid;
- private final boolean mShouldTrack;
- private boolean mChanged = false;
+ private final class UpdateJobFunctor implements JobStore.JobStatusFunctor {
+ private final int mFilterUid;
- UpdateTrackedJobsFunc(int uid, String packageName, boolean shouldTrack) {
- mUid = uid;
- mPackageName = packageName;
- mShouldTrack = shouldTrack;
+ boolean mChanged = false;
+ int mTotalCount = 0;
+ int mCheckedCount = 0;
+
+ UpdateJobFunctor(int filterUid) {
+ mFilterUid = filterUid;
}
@Override
public void process(JobStatus jobStatus) {
- final String packageName = jobStatus.getSourcePackageName();
- final int uid = jobStatus.getSourceUid();
- if (mUid != uid || !mPackageName.equals(packageName)) {
+ mTotalCount++;
+ if ((mFilterUid > 0) && (mFilterUid != jobStatus.getSourceUid())) {
return;
}
- if (mShouldTrack) {
- mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
- canRunJobLocked(uid));
- startTrackingJobLocked(jobStatus);
- } else {
- mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
- stopTrackingJobLocked(jobStatus);
+ mCheckedCount++;
+ if (updateSingleJobRestrictionLocked(jobStatus)) {
+ mChanged = true;
}
}
}
+
+ private final Listener mForceAppStandbyListener = new Listener() {
+ @Override
+ public void onRestrictionChanged(int uid, String packageName) {
+ updateJobRestrictionsForUidLocked(uid);
+ }
+
+ @Override
+ public void onGlobalRestrictionChanged() {
+ updateAllJobRestrictionsLocked();
+ }
+ };
}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 5e9f3550635f..da481a8c2b47 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -18,15 +18,25 @@ package com.android.server.location;
import android.Manifest;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.contexthub.V1_0.AsyncEventType;
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.IContexthubCallback;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
import android.hardware.location.ContextHubInfo;
-import android.hardware.location.ContextHubManager;
import android.hardware.location.ContextHubMessage;
-import android.hardware.location.IContextHubService;
import android.hardware.location.IContextHubCallback;
-import android.hardware.location.NanoAppFilter;
+import android.hardware.location.IContextHubService;
+import android.hardware.location.IContextHubTransactionCallback;
import android.hardware.location.NanoApp;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppFilter;
import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppState;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
@@ -38,8 +48,10 @@ import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.HashMap;
/**
* @hide
@@ -48,55 +60,152 @@ public class ContextHubService extends IContextHubService.Stub {
private static final String TAG = "ContextHubService";
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
- + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
-
- public static final int ANY_HUB = -1;
- public static final int MSG_LOAD_NANO_APP = 3;
+ + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+
+ /*
+ * Constants for the type of transaction that is defined by ContextHubService.
+ * This is used to report the transaction callback to clients, and is different from
+ * ContextHubTransaction.Type.
+ */
+ public static final int MSG_ENABLE_NANO_APP = 1;
+ public static final int MSG_DISABLE_NANO_APP = 2;
+ public static final int MSG_LOAD_NANO_APP = 3;
public static final int MSG_UNLOAD_NANO_APP = 4;
+ public static final int MSG_QUERY_NANO_APPS = 5;
+ public static final int MSG_QUERY_MEMORY = 6;
+ public static final int MSG_HUB_RESET = 7;
private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
private static final int PRE_LOADED_APP_MEM_REQ = 0;
- private static final int MSG_HEADER_SIZE = 4;
- private static final int HEADER_FIELD_MSG_TYPE = 0;
- private static final int HEADER_FIELD_MSG_VERSION = 1;
- private static final int HEADER_FIELD_HUB_HANDLE = 2;
- private static final int HEADER_FIELD_APP_INSTANCE = 3;
-
- private static final int HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
- private static final int HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
- private static final int MSG_LOAD_APP_HEADER_SIZE = MSG_HEADER_SIZE + 2;
-
private static final int OS_APP_INSTANCE = -1;
private final Context mContext;
+
+ // TODO(b/69270990): Remove once old ContextHubManager API is deprecated
+ // Service cache maintaining of instance ID to nanoapp infos
private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash =
new ConcurrentHashMap<>();
+ // The next available instance ID (managed by the service) to assign to a nanoapp
+ private int mNextAvailableInstanceId = 0;
+ // A map of the long nanoapp ID to instance ID managed by the service
+ private final ConcurrentHashMap<Long, Integer> mNanoAppIdToInstanceMap =
+ new ConcurrentHashMap<>();
+
private final ContextHubInfo[] mContextHubInfo;
private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
new RemoteCallbackList<>();
- private native int nativeSendMessage(int[] header, byte[] data);
- private native ContextHubInfo[] nativeInitialize();
+ // Proxy object to communicate with the Context Hub HAL
+ private final IContexthub mContextHubProxy;
+
+ // The manager for transaction queue
+ private final ContextHubTransactionManager mTransactionManager;
+
+ /**
+ * Class extending the callback to register with a Context Hub.
+ */
+ private class ContextHubServiceCallback extends IContexthubCallback.Stub {
+ private final int mContextHubId;
+
+ ContextHubServiceCallback(int contextHubId) {
+ mContextHubId = contextHubId;
+ }
+
+ @Override
+ public void handleClientMsg(ContextHubMsg message) {
+ handleClientMessageCallback(mContextHubId, message);
+ }
+
+ @Override
+ public void handleTxnResult(int transactionId, int result) {
+ handleTransactionResultCallback(mContextHubId, transactionId, result);
+ }
+
+ @Override
+ public void handleHubEvent(int eventType) {
+ handleHubEventCallback(mContextHubId, eventType);
+ }
+
+ @Override
+ public void handleAppAbort(long nanoAppId, int abortCode) {
+ handleAppAbortCallback(mContextHubId, nanoAppId, abortCode);
+ }
+
+ @Override
+ public void handleAppsInfo(ArrayList<HubAppInfo> nanoAppInfoList) {
+ handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
+ }
+ }
public ContextHubService(Context context) {
mContext = context;
- mContextHubInfo = nativeInitialize();
+
+ mContextHubProxy = getContextHubProxy();
+ if (mContextHubProxy == null) {
+ mTransactionManager = null;
+ mContextHubInfo = new ContextHubInfo[0];
+ return;
+ }
+
+ mTransactionManager = new ContextHubTransactionManager(mContextHubProxy);
+
+ List<ContextHub> hubList;
+ try {
+ hubList = mContextHubProxy.getHubs();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while getting Context Hub info");
+ hubList = Collections.emptyList();
+ }
+ mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
+
+ for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+ int contextHubId = contextHubInfo.getId();
+ try {
+ mContextHubProxy.registerCallback(
+ contextHubId, new ContextHubServiceCallback(contextHubId));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
+ + contextHubId + ")");
+ }
+ }
+
+ // Do a query to initialize the service cache list of nanoapps
+ // TODO(b/69270990): Remove this when old API is deprecated
+ for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+ queryNanoAppsInternal(contextHubInfo.getId());
+ }
for (int i = 0; i < mContextHubInfo.length; i++) {
Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
- + ", name: " + mContextHubInfo[i].getName());
+ + ", name: " + mContextHubInfo[i].getName());
}
}
+ /**
+ * @return the IContexthub proxy interface
+ */
+ private IContexthub getContextHubProxy() {
+ IContexthub proxy = null;
+ try {
+ proxy = IContexthub.getService(true /* retry */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy");
+ } catch (NoSuchElementException e) {
+ Log.i(TAG, "Context Hub HAL service not found");
+ }
+
+ return proxy;
+ }
+
@Override
public int registerCallback(IContextHubCallback callback) throws RemoteException {
checkPermissions();
mCallbacksList.register(callback);
Log.d(TAG, "Added callback, total callbacks " +
- mCallbacksList.getRegisteredCallbackCount());
+ mCallbacksList.getRegisteredCallbackCount());
return 0;
}
@@ -109,29 +218,112 @@ public class ContextHubService extends IContextHubService.Stub {
for (int i = 0; i < returnArray.length; ++i) {
returnArray[i] = i;
Log.d(TAG, String.format("Hub %s is mapped to %d",
- mContextHubInfo[i].getName(), returnArray[i]));
+ mContextHubInfo[i].getName(), returnArray[i]));
}
return returnArray;
}
@Override
- public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
+ public ContextHubInfo getContextHubInfo(int contextHubId) throws RemoteException {
checkPermissions();
- if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
- Log.e(TAG, "Invalid context hub handle " + contextHubHandle);
+ if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+ Log.e(TAG, "Invalid context hub handle " + contextHubId);
return null; // null means fail
}
- return mContextHubInfo[contextHubHandle];
+ return mContextHubInfo[contextHubId];
+ }
+
+ /**
+ * Creates an internal load transaction callback to be used for old API clients
+ *
+ * @param contextHubId the ID of the hub to load the binary
+ * @param nanoAppBinary the binary to load
+ * @return the callback interface
+ */
+ private IContextHubTransactionCallback createLoadTransactionCallback(
+ int contextHubId, NanoAppBinary nanoAppBinary) {
+ return new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onTransactionComplete(int result) {
+ handleLoadResponseOldApi(contextHubId, result, nanoAppBinary);
+ }
+
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ }
+ };
+ }
+
+ /**
+ * Creates an internal unload transaction callback to be used for old API clients
+ *
+ * @param contextHubId the ID of the hub to unload the nanoapp
+ * @param nanoAppId the ID of the nanoapp to unload
+ * @return the callback interface
+ */
+ private IContextHubTransactionCallback createUnloadTransactionCallback(
+ int contextHubId, long nanoAppId) {
+ return new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onTransactionComplete(int result) {
+ handleUnloadResponseOldApi(contextHubId, result, nanoAppId);
+ }
+
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ }
+ };
+ }
+
+ /**
+ * Creates an internal query transaction callback to be used for old API clients
+ *
+ * @param contextHubId the ID of the hub to query
+ * @return the callback interface
+ */
+ private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) {
+ return new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onTransactionComplete(int result) {
+ }
+
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ byte[] data = {(byte) result};
+ onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
+ }
+ };
+ }
+
+ /**
+ * Adds a new transaction to the transaction manager queue
+ *
+ * @param transaction the transaction to add
+ * @return the result of adding the transaction
+ */
+ private int addTransaction(ContextHubServiceTransaction transaction) {
+ int result = Result.OK;
+ try {
+ mTransactionManager.addTransaction(transaction);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, e.getMessage());
+ result = Result.TRANSACTION_PENDING; /* failed */
+ }
+
+ return result;
}
@Override
- public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException {
+ public int loadNanoApp(int contextHubId, NanoApp app) throws RemoteException {
checkPermissions();
+ if (mContextHubProxy == null) {
+ return -1;
+ }
- if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
- Log.e(TAG, "Invalid contextHubhandle " + contextHubHandle);
+ if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+ Log.e(TAG, "Invalid contextHubhandle " + contextHubId);
return -1;
}
if (app == null) {
@@ -139,20 +331,17 @@ public class ContextHubService extends IContextHubService.Stub {
return -1;
}
- int[] msgHeader = new int[MSG_LOAD_APP_HEADER_SIZE];
- msgHeader[HEADER_FIELD_HUB_HANDLE] = contextHubHandle;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
- msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
- msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP;
-
- long appId = app.getAppId();
+ // Create an internal IContextHubTransactionCallback for the old API clients
+ NanoAppBinary nanoAppBinary = new NanoAppBinary(app.getAppBinary());
+ IContextHubTransactionCallback onCompleteCallback =
+ createLoadTransactionCallback(contextHubId, nanoAppBinary);
- msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF);
- msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF);
+ ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
+ contextHubId, nanoAppBinary, onCompleteCallback);
- int errVal = nativeSendMessage(msgHeader, app.getAppBinary());
- if (errVal != 0) {
- Log.e(TAG, "Send Message returns error" + contextHubHandle);
+ int result = addTransaction(transaction);
+ if (result != Result.OK) {
+ Log.e(TAG, "Failed to load nanoapp with error code " + result);
return -1;
}
@@ -163,23 +352,26 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException {
checkPermissions();
+ if (mContextHubProxy == null) {
+ return -1;
+ }
+
NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle);
if (info == null) {
Log.e(TAG, "Cannot find app with handle " + nanoAppInstanceHandle);
return -1; //means failed
}
- // Call Native interface here
- int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppInstanceHandle;
- msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
- msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_UNLOAD_NANO_APP;
-
- byte msg[] = new byte[0];
+ int contextHubId = info.getContexthubId();
+ long nanoAppId = info.getAppId();
+ IContextHubTransactionCallback onCompleteCallback =
+ createUnloadTransactionCallback(contextHubId, nanoAppId);
+ ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
+ contextHubId, nanoAppId, onCompleteCallback);
- if (nativeSendMessage(msgHeader, msg) != 0) {
- Log.e(TAG, "native send message fails");
+ int result = addTransaction(transaction);
+ if (result != Result.OK) {
+ Log.e(TAG, "Failed to unload nanoapp with error code " + result);
return -1;
}
@@ -189,7 +381,7 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
- throws RemoteException {
+ throws RemoteException {
checkPermissions();
// This assumes that all the nanoAppInfo is current. This is reasonable
// for the use cases for tightly controlled nanoApps.
@@ -206,7 +398,7 @@ public class ContextHubService extends IContextHubService.Stub {
checkPermissions();
ArrayList<Integer> foundInstances = new ArrayList<Integer>();
- for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+ for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
if (filter.testMatch(info)) {
@@ -223,23 +415,230 @@ public class ContextHubService extends IContextHubService.Stub {
return retArray;
}
+ /**
+ * Performs a query at the specified hub.
+ *
+ * This method should only be invoked internally by the service, either to update the service
+ * cache or as a result of an explicit query requested by a client through the sendMessage API.
+ *
+ * @param contextHubId the ID of the hub to do the query
+ * @return the result of the query
+ */
+ private int queryNanoAppsInternal(int contextHubId) {
+ if (mContextHubProxy == null) {
+ return Result.UNKNOWN_FAILURE;
+ }
+
+ IContextHubTransactionCallback onCompleteCallback =
+ createQueryTransactionCallback(contextHubId);
+ ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+ contextHubId, onCompleteCallback);
+
+ return addTransaction(transaction);
+ }
+
@Override
- public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
- throws RemoteException {
+ public int sendMessage(
+ int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException {
checkPermissions();
-
- if (msg == null || msg.getData() == null) {
- Log.w(TAG, "null ptr");
+ if (mContextHubProxy == null) {
+ return -1;
+ }
+ if (msg == null) {
+ Log.e(TAG, "ContextHubMessage cannot be null");
return -1;
}
+ if (msg.getData() == null) {
+ Log.w(TAG, "ContextHubMessage message body cannot be null");
+ return -1;
+ }
+
+ int result;
+ if (nanoAppHandle == OS_APP_INSTANCE) {
+ if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
+ result = queryNanoAppsInternal(hubHandle);
+ } else {
+ Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
+ result = Result.BAD_PARAMS;
+ }
+ } else {
+ NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
+ if (info != null) {
+ ContextHubMsg hubMessage = new ContextHubMsg();
+ hubMessage.appName = info.getAppId();
+ hubMessage.msgType = msg.getMsgType();
+ hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED;
+ ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg);
+
+ try {
+ result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send nanoapp message - RemoteException");
+ result = Result.UNKNOWN_FAILURE;
+ }
+ } else {
+ Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
+ + nanoAppHandle + " does not exist.");
+ result = Result.BAD_PARAMS;
+ }
+ }
- int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[HEADER_FIELD_HUB_HANDLE] = hubHandle;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppHandle;
- msgHeader[HEADER_FIELD_MSG_VERSION] = msg.getVersion();
- msgHeader[HEADER_FIELD_MSG_TYPE] = msg.getMsgType();
+ return (result == Result.OK ? 0 : -1);
+ }
- return nativeSendMessage(msgHeader, msg.getData());
+ /**
+ * Handles a unicast or broadcast message from a nanoapp.
+ *
+ * @param contextHubId the ID of the hub the message came from
+ * @param message the message contents
+ */
+ private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
+ // TODO(b/67734082): Send to new API clients
+ byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg);
+
+ int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ?
+ mNanoAppIdToInstanceMap.get(message.appName) : -1;
+ onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data);
+ }
+
+ /**
+ * A helper function to handle a load response from the Context Hub for the old API.
+ *
+ * TODO(b/69270990): Remove this once the old APIs are obsolete.
+ */
+ private void handleLoadResponseOldApi(
+ int contextHubId, int result, NanoAppBinary nanoAppBinary) {
+ if (nanoAppBinary == null) {
+ Log.e(TAG, "Nanoapp binary field was null for a load transaction");
+ return;
+ }
+
+ // NOTE: The legacy JNI code used to do a query right after a load success
+ // to synchronize the service cache. Instead store the binary that was requested to
+ // load to update the cache later without doing a query.
+ int instanceId = 0;
+ long nanoAppId = nanoAppBinary.getNanoAppId();
+ int nanoAppVersion = nanoAppBinary.getNanoAppVersion();
+ if (result == TransactionResult.SUCCESS) {
+ if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+ instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+ } else {
+ instanceId = mNextAvailableInstanceId++;
+ mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+ }
+
+ addAppInstance(contextHubId, instanceId, nanoAppId, nanoAppVersion);
+ }
+
+ byte[] data = new byte[5];
+ data[0] = (byte) result;
+ ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
+
+ onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+ }
+
+ /**
+ * A helper function to handle an unload response from the Context Hub for the old API.
+ *
+ * TODO(b/69270990): Remove this once the old APIs are obsolete.
+ */
+ private void handleUnloadResponseOldApi(
+ int contextHubId, int result, long nanoAppId) {
+ if (result == TransactionResult.SUCCESS) {
+ int instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+ deleteAppInstance(instanceId);
+ mNanoAppIdToInstanceMap.remove(nanoAppId);
+ }
+
+ byte[] data = new byte[1];
+ data[0] = (byte) result;
+ onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+ }
+
+ /**
+ * Handles a transaction response from a Context Hub.
+ *
+ * @param contextHubId the ID of the hub the response came from
+ * @param transactionId the ID of the transaction
+ * @param result the result of the transaction reported by the hub
+ */
+ private void handleTransactionResultCallback(int contextHubId, int transactionId, int result) {
+ mTransactionManager.onTransactionResponse(transactionId, result);
+ }
+
+ /**
+ * Handles an asynchronous event from a Context Hub.
+ *
+ * @param contextHubId the ID of the hub the response came from
+ * @param eventType the type of the event as defined in Context Hub HAL AsyncEventType
+ */
+ private void handleHubEventCallback(int contextHubId, int eventType) {
+ if (eventType == AsyncEventType.RESTARTED) {
+ mTransactionManager.onHubReset();
+ queryNanoAppsInternal(contextHubId);
+
+ byte[] data = {TransactionResult.SUCCESS};
+ onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+ } else {
+ Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
+ + eventType + ")");
+ }
+ }
+
+ /**
+ * Handles an asynchronous abort event of a nanoapp.
+ *
+ * @param contextHubId the ID of the hub that the nanoapp aborted in
+ * @param nanoAppId the ID of the aborted nanoapp
+ * @param abortCode the nanoapp-specific abort code
+ */
+ private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) {
+ // TODO(b/31049861): Implement this
+ }
+
+ /**
+ * Handles a query response from a Context Hub.
+ *
+ * @param contextHubId the ID of the hub of the response
+ * @param nanoAppInfoList the list of loaded nanoapps
+ */
+ private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+ List<NanoAppState> nanoAppStateList =
+ ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
+
+ updateServiceCache(contextHubId, nanoAppInfoList);
+ mTransactionManager.onQueryResponse(nanoAppStateList);
+ }
+
+ /**
+ * Updates the service's cache of the list of loaded nanoapps using a nanoapp list response.
+ *
+ * TODO(b/69270990): Remove this when the old API functionality is removed.
+ *
+ * @param contextHubId the ID of the hub the response came from
+ * @param nanoAppInfoList the list of loaded nanoapps
+ */
+ private void updateServiceCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+ synchronized (mNanoAppHash) {
+ for (int instanceId : mNanoAppHash.keySet()) {
+ if (mNanoAppHash.get(instanceId).getContexthubId() == contextHubId) {
+ deleteAppInstance(instanceId);
+ }
+ }
+
+ for (HubAppInfo appInfo : nanoAppInfoList) {
+ int instanceId;
+ long nanoAppId = appInfo.appId;
+ if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+ instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+ } else {
+ instanceId = mNextAvailableInstanceId++;
+ mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+ }
+
+ addAppInstance(contextHubId, instanceId, nanoAppId, appInfo.version);
+ }
+ }
}
@Override
@@ -257,7 +656,7 @@ public class ContextHubService extends IContextHubService.Stub {
pw.println("");
pw.println("=================== NANOAPPS ====================");
// Dump nanoAppHash
- for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+ for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
}
@@ -268,19 +667,15 @@ public class ContextHubService extends IContextHubService.Stub {
mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
}
- private int onMessageReceipt(int[] header, byte[] data) {
- if (header == null || data == null || header.length < MSG_HEADER_SIZE) {
- return -1;
+ private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) {
+ if (data == null) {
+ return -1;
}
+ int msgVersion = 0;
int callbacksCount = mCallbacksList.beginBroadcast();
- int msgType = header[HEADER_FIELD_MSG_TYPE];
- int msgVersion = header[HEADER_FIELD_MSG_VERSION];
- int hubHandle = header[HEADER_FIELD_HUB_HANDLE];
- int appInstance = header[HEADER_FIELD_APP_INSTANCE];
-
Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
- hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
+ hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
if (callbacksCount < 1) {
Log.v(TAG, "No message callbacks registered.");
@@ -323,8 +718,8 @@ public class ContextHubService extends IContextHubService.Stub {
}
mNanoAppHash.put(appInstanceHandle, appInfo);
- Log.d(TAG, action + " app instance " + appInstanceHandle + " with id "
- + appId + " version " + appVersion);
+ Log.d(TAG, action + " app instance " + appInstanceHandle + " with id 0x"
+ + Long.toHexString(appId) + " version 0x" + Integer.toHexString(appVersion));
return 0;
}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
new file mode 100644
index 000000000000..66145bbb9784
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppState;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An abstract class representing transactions requested to the Context Hub Service.
+ *
+ * @hide
+ */
+/* package */ abstract class ContextHubServiceTransaction {
+ private final int mTransactionId;
+ @ContextHubTransaction.Type
+ private final int mTransactionType;
+
+ /*
+ * true if the transaction has already completed, false otherwise
+ */
+ private boolean mIsComplete = false;
+
+ /* package */ ContextHubServiceTransaction(int id, int type) {
+ mTransactionId = id;
+ mTransactionType = type;
+ }
+
+ /**
+ * Starts this transaction with a Context Hub.
+ *
+ * All instances of this class must implement this method by making an asynchronous request to
+ * a hub.
+ *
+ * @return the synchronous error code of the transaction start
+ */
+ /* package */
+ abstract int onTransact();
+
+ /**
+ * A function to invoke when a transaction times out.
+ *
+ * All instances of this class must implement this method by reporting the timeout to the
+ * client.
+ */
+ /* package */
+ abstract void onTimeout();
+
+ /**
+ * A function to invoke when the transaction completes.
+ *
+ * Only relevant for load, unload, enable, or disable transactions.
+ *
+ * @param result the result of the transaction
+ */
+ /* package */ void onTransactionComplete(int result) {
+ }
+
+ /**
+ * A function to invoke when a query transaction completes.
+ *
+ * Only relevant for query transactions.
+ *
+ * @param result the result of the query
+ * @param nanoAppStateList the list of nanoapps given by the query response
+ */
+ /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ }
+
+ /**
+ * @return the ID of this transaction
+ */
+ /* package */ int getTransactionId() {
+ return mTransactionId;
+ }
+
+ /**
+ * @return the type of this transaction
+ * @see ContextHubTransaction.Type
+ */
+ @ContextHubTransaction.Type
+ /* package */ int getTransactionType() {
+ return mTransactionType;
+ }
+
+ /**
+ * Gets the timeout period as defined in IContexthub.hal
+ *
+ * @return the timeout of this transaction in the specified time unit
+ */
+ /* package */ long getTimeout(TimeUnit unit) {
+ switch (mTransactionType) {
+ case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+ return unit.convert(30L, TimeUnit.SECONDS);
+ case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+ case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+ case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+ case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+ // Note: query timeout is not specified at the HAL
+ default: /* fall through */
+ return unit.convert(5L, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Marks the transaction as complete.
+ *
+ * Should only be called as a result of a response from a Context Hub callback
+ */
+ /* package */ void setComplete() {
+ mIsComplete = true;
+ }
+
+ /**
+ * @return true if the transaction has already completed, false otherwise
+ */
+ /* package */ boolean isComplete() {
+ return mIsComplete;
+ }
+
+ /**
+ * @return the human-readable string of this transaction's type
+ */
+ private String getTransactionTypeString() {
+ switch (mTransactionType) {
+ case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+ return "Load";
+ case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+ return "Unload";
+ case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+ return "Enable";
+ case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+ return "Disable";
+ case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+ return "Query";
+ default:
+ return "Unknown";
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getTransactionTypeString() + " transaction (ID = " + mTransactionId + ")";
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
new file mode 100644
index 000000000000..ddbaf86beff2
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A class encapsulating helper functions used by the ContextHubService class
+ */
+/* package */ class ContextHubServiceUtil {
+ private static final String TAG = "ContextHubServiceUtil";
+
+ /**
+ * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
+ *
+ * @param hubList the ContextHub ArrayList
+ * @return the ContextHubInfo array
+ */
+ /* package */
+ static ContextHubInfo[] createContextHubInfoArray(List<ContextHub> hubList) {
+ ContextHubInfo[] contextHubInfoList = new ContextHubInfo[hubList.size()];
+ for (int i = 0; i < hubList.size(); i++) {
+ contextHubInfoList[i] = new ContextHubInfo(hubList.get(i));
+ }
+
+ return contextHubInfoList;
+ }
+
+ /**
+ * Copies a primitive byte array to a ArrayList<Byte>.
+ *
+ * @param inputArray the primitive byte array
+ * @param outputArray the ArrayList<Byte> array to append
+ */
+ /* package */
+ static void copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray) {
+ outputArray.clear();
+ outputArray.ensureCapacity(inputArray.length);
+ for (byte element : inputArray) {
+ outputArray.add(element);
+ }
+ }
+
+ /**
+ * Creates a byte array given a ArrayList<Byte> and copies its contents.
+ *
+ * @param array the ArrayList<Byte> object
+ * @return the byte array
+ */
+ /* package */
+ static byte[] createPrimitiveByteArray(ArrayList<Byte> array) {
+ byte[] primitiveArray = new byte[array.size()];
+ for (int i = 0; i < array.size(); i++) {
+ primitiveArray[i] = array.get(i);
+ }
+
+ return primitiveArray;
+ }
+
+ /**
+ * Generates the Context Hub HAL's NanoAppBinary object from the client-facing
+ * android.hardware.location.NanoAppBinary object.
+ *
+ * @param nanoAppBinary the client-facing NanoAppBinary object
+ * @return the Context Hub HAL's NanoAppBinary object
+ */
+ /* package */
+ static android.hardware.contexthub.V1_0.NanoAppBinary createHidlNanoAppBinary(
+ NanoAppBinary nanoAppBinary) {
+ android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+ new android.hardware.contexthub.V1_0.NanoAppBinary();
+
+ hidlNanoAppBinary.appId = nanoAppBinary.getNanoAppId();
+ hidlNanoAppBinary.appVersion = nanoAppBinary.getNanoAppVersion();
+ hidlNanoAppBinary.flags = nanoAppBinary.getFlags();
+ hidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion();
+ hidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion();
+
+ // Log exceptions while processing the binary, but continue to pass down the binary
+ // since the error checking is deferred to the Context Hub.
+ try {
+ copyToByteArrayList(nanoAppBinary.getBinaryNoHeader(), hidlNanoAppBinary.customBinary);
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, e.getMessage());
+ } catch (NullPointerException e) {
+ Log.w(TAG, "NanoApp binary was null");
+ }
+
+ return hidlNanoAppBinary;
+ }
+
+ /**
+ * Generates a client-facing NanoAppState array from a HAL HubAppInfo array.
+ *
+ * @param nanoAppInfoList the array of HubAppInfo objects
+ * @return the corresponding array of NanoAppState objects
+ */
+ /* package */
+ static List<NanoAppState> createNanoAppStateList(
+ List<HubAppInfo> nanoAppInfoList) {
+ ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>();
+ for (HubAppInfo appInfo : nanoAppInfoList) {
+ nanoAppStateList.add(
+ new NanoAppState(appInfo.appId, appInfo.version, appInfo.enabled));
+ }
+
+ return nanoAppStateList;
+ }
+
+ /**
+ * Creates a HIDL ContextHubMsg object to send to a nanoapp.
+ *
+ * @param hostEndPoint the ID of the client sending the message
+ * @param message the client-facing NanoAppMessage object describing the message
+ * @return the HIDL ContextHubMsg object
+ */
+ /* package */
+ static ContextHubMsg createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message) {
+ ContextHubMsg hidlMessage = new ContextHubMsg();
+
+ hidlMessage.appName = message.getNanoAppId();
+ hidlMessage.hostEndPoint = hostEndPoint;
+ hidlMessage.msgType = message.getMessageType();
+ copyToByteArrayList(message.getMessageBody(), hidlMessage.msg);
+
+ return hidlMessage;
+ }
+
+ /**
+ * Creates a client-facing NanoAppMessage object to send to a client.
+ *
+ * @param message the HIDL ContextHubMsg object from a nanoapp
+ * @return the NanoAppMessage object
+ */
+ /* package */
+ static NanoAppMessage createNanoAppMessage(ContextHubMsg message) {
+ byte[] messageArray = createPrimitiveByteArray(message.msg);
+
+ return NanoAppMessage.createMessageFromNanoApp(
+ message.appName, message.msgType, messageArray,
+ message.hostEndPoint == HostEndPoint.BROADCAST);
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
new file mode 100644
index 000000000000..898b76c9d820
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppState;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages transactions at the Context Hub Service.
+ *
+ * This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ * and executes them through the Context Hub. At any point in time, either the transaction queue is
+ * empty, or there is a pending transaction that is waiting for an asynchronous response from the
+ * hub. This class also handles synchronous errors and timeouts of each transaction.
+ *
+ * @hide
+ */
+/* package */ class ContextHubTransactionManager {
+ private static final String TAG = "ContextHubTransactionManager";
+
+ /*
+ * Maximum number of transaction requests that can be pending at a time
+ */
+ private static final int MAX_PENDING_REQUESTS = 10;
+
+ /*
+ * The proxy to talk to the Context Hub
+ */
+ private final IContexthub mContextHubProxy;
+
+ /*
+ * A queue containing the current transactions
+ */
+ private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
+
+ /*
+ * The next available transaction ID
+ */
+ private final AtomicInteger mNextAvailableId = new AtomicInteger();
+
+ /*
+ * An executor and the future object for scheduling timeout timers
+ */
+ private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
+ private ScheduledFuture<?> mTimeoutFuture = null;
+
+ /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) {
+ mContextHubProxy = contextHubProxy;
+ }
+
+ /**
+ * Creates a transaction for loading a nanoapp.
+ *
+ * @param contextHubId the ID of the hub to load the nanoapp to
+ * @param nanoAppBinary the binary of the nanoapp to load
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createLoadTransaction(
+ int contextHubId, NanoAppBinary nanoAppBinary,
+ IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP) {
+ @Override
+ /* package */ int onTransact() {
+ android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+ ContextHubServiceUtil.createHidlNanoAppBinary(nanoAppBinary);
+ try {
+ return mContextHubProxy.loadNanoApp(
+ contextHubId, hidlNanoAppBinary, this.getTransactionId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to load nanoapp with ID 0x" +
+ Long.toHexString(nanoAppBinary.getNanoAppId()));
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTimeout() {
+ onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+ }
+
+ @Override
+ /* package */ void onTransactionComplete(int result) {
+ try {
+ onCompleteCallback.onTransactionComplete(result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a transaction for unloading a nanoapp.
+ *
+ * @param contextHubId the ID of the hub to load the nanoapp to
+ * @param nanoAppId the ID of the nanoapp to unload
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createUnloadTransaction(
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP) {
+ @Override
+ /* package */ int onTransact() {
+ try {
+ return mContextHubProxy.unloadNanoApp(
+ contextHubId, nanoAppId, this.getTransactionId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to unload nanoapp with ID 0x" +
+ Long.toHexString(nanoAppId));
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTimeout() {
+ onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+ }
+
+ @Override
+ /* package */ void onTransactionComplete(int result) {
+ try {
+ onCompleteCallback.onTransactionComplete(result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a transaction for querying for a list of nanoapps.
+ *
+ * @param contextHubId the ID of the hub to query
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createQueryTransaction(
+ int contextHubId, IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+ @Override
+ /* package */ int onTransact() {
+ try {
+ return mContextHubProxy.queryApps(contextHubId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to query for nanoapps");
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTimeout() {
+ onQueryResponse(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT,
+ Collections.emptyList());
+ }
+
+ @Override
+ /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ try {
+ onCompleteCallback.onQueryResponse(result, nanoAppStateList);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onQueryComplete");
+ }
+ }
+ };
+ }
+
+ /**
+ * Adds a new transaction to the queue.
+ *
+ * If there was no pending transaction at the time, the transaction that was added will be
+ * started in this method.
+ *
+ * @param transaction the transaction to add
+ * @throws IllegalStateException if the queue is full
+ */
+ /* package */
+ synchronized void addTransaction(
+ ContextHubServiceTransaction transaction) throws IllegalStateException {
+ if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
+ throw new IllegalStateException("Transaction transaction queue is full (capacity = "
+ + MAX_PENDING_REQUESTS + ")");
+ }
+ mTransactionQueue.add(transaction);
+
+ if (mTransactionQueue.size() == 1) {
+ startNextTransaction();
+ }
+ }
+
+ /**
+ * Handles a transaction response from a Context Hub.
+ *
+ * @param transactionId the transaction ID of the response
+ * @param result the result of the transaction
+ */
+ /* package */
+ synchronized void onTransactionResponse(int transactionId, int result) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+ return;
+ }
+ if (transaction.getTransactionId() != transactionId) {
+ Log.w(TAG, "Received unexpected transaction response (expected ID = "
+ + transaction.getTransactionId() + ", received ID = " + transactionId + ")");
+ return;
+ }
+
+ transaction.onTransactionComplete(result);
+ removeTransactionAndStartNext();
+ }
+
+ /**
+ * Handles a query response from a Context Hub.
+ *
+ * @param nanoAppStateList the list of nanoapps included in the response
+ */
+ /* package */
+ synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected query response (no transaction pending)");
+ return;
+ }
+ if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+ Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
+ return;
+ }
+
+ transaction.onQueryResponse(TransactionResult.SUCCESS, nanoAppStateList);
+ removeTransactionAndStartNext();
+ }
+
+ /**
+ * Handles a hub reset event by stopping a pending transaction and starting the next.
+ */
+ /* package */
+ synchronized void onHubReset() {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ return;
+ }
+
+ removeTransactionAndStartNext();
+ }
+
+ /**
+ * Pops the front transaction from the queue and starts the next pending transaction request.
+ *
+ * Removing elements from the transaction queue must only be done through this method. When a
+ * pending transaction is removed, the timeout timer is cancelled and the transaction is marked
+ * complete.
+ *
+ * It is assumed that the transaction queue is non-empty when this method is invoked, and that
+ * the caller has obtained a lock on this ContextHubTransactionManager object.
+ */
+ private void removeTransactionAndStartNext() {
+ mTimeoutFuture.cancel(false /* mayInterruptIfRunning */);
+
+ ContextHubServiceTransaction transaction = mTransactionQueue.remove();
+ transaction.setComplete();
+
+ if (!mTransactionQueue.isEmpty()) {
+ startNextTransaction();
+ }
+ }
+
+ /**
+ * Starts the next pending transaction request.
+ *
+ * Starting new transactions must only be done through this method. This method continues to
+ * process the transaction queue as long as there are pending requests, and no transaction is
+ * pending.
+ *
+ * It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
+ * object.
+ */
+ private void startNextTransaction() {
+ int result = Result.UNKNOWN_FAILURE;
+ while (result != Result.OK && !mTransactionQueue.isEmpty()) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ result = transaction.onTransact();
+
+ if (result == Result.OK) {
+ Runnable onTimeoutFunc = () -> {
+ synchronized (this) {
+ if (!transaction.isComplete()) {
+ Log.d(TAG, transaction + " timed out");
+ transaction.onTimeout();
+
+ removeTransactionAndStartNext();
+ }
+ }
+ };
+
+ long timeoutSeconds = transaction.getTimeout(TimeUnit.SECONDS);
+ mTimeoutFuture = mTimeoutExecutor.schedule(onTimeoutFunc, timeoutSeconds,
+ TimeUnit.SECONDS);
+ } else {
+ mTransactionQueue.remove();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index be223f1e56df..d71c3b04d22e 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -16,7 +16,7 @@
package com.android.server.media;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.content.Context;
import android.media.AudioPlaybackConfiguration;
import android.media.IAudioService;
@@ -27,14 +27,15 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.HashMap;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -49,47 +50,57 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor();
/**
- * Called when the state of audio player is changed.
+ * Listener for handling the active state changes of audio players.
*/
- interface OnAudioPlayerStateChangedListener {
- void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config);
+ interface OnAudioPlayerActiveStateChangedListener {
+ /**
+ * Called when the active state of audio player is changed.
+ *
+ * @param config The audio playback configuration for the audio player of which active state
+ * was changed. If {@param isRemoved} is {@code true}, this hold outdated
+ * information.
+ * @param isRemoved {@code true} if the audio player is removed.
+ */
+ void onAudioPlayerActiveStateChanged(
+ @NonNull AudioPlaybackConfiguration config, boolean isRemoved);
}
private final static class MessageHandler extends Handler {
- private static final int MSG_AUDIO_PLAYER_STATE_CHANGED = 1;
+ private static final int MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED = 1;
- private final OnAudioPlayerStateChangedListener mListsner;
+ private final OnAudioPlayerActiveStateChangedListener mListener;
- public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) {
+ public MessageHandler(Looper looper, OnAudioPlayerActiveStateChangedListener listener) {
super(looper);
- mListsner = listener;
+ mListener = listener;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_AUDIO_PLAYER_STATE_CHANGED:
- mListsner.onAudioPlayerStateChanged(
- msg.arg1, msg.arg2, (AudioPlaybackConfiguration) msg.obj);
+ case MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED:
+ mListener.onAudioPlayerActiveStateChanged((AudioPlaybackConfiguration) msg.obj,
+ msg.arg1 != 0);
break;
}
}
- public void sendAudioPlayerStateChangedMessage(int uid, int prevState,
- AudioPlaybackConfiguration config) {
- obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget();
+ public void sendAudioPlayerActiveStateChangedMessage(
+ final AudioPlaybackConfiguration config, final boolean isRemoved) {
+ obtainMessage(MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED,
+ isRemoved ? 1 : 0, 0 /* unused */, config).sendToTarget();
}
}
private final Object mLock = new Object();
@GuardedBy("mLock")
- private final Map<OnAudioPlayerStateChangedListener, MessageHandler> mListenerMap =
- new HashMap<>();
+ private final Map<OnAudioPlayerActiveStateChangedListener, MessageHandler> mListenerMap =
+ new ArrayMap<>();
@GuardedBy("mLock")
- private final Map<Integer, Integer> mAudioPlayerStates = new HashMap<>();
+ private final Set<Integer> mActiveAudioUids = new ArraySet();
@GuardedBy("mLock")
- private final Map<Integer, HashSet<Integer>> mAudioPlayersForUid = new HashMap<>();
+ private ArrayMap<Integer, AudioPlaybackConfiguration> mPrevActiveAudioPlaybackConfigs =
+ new ArrayMap<>();
// Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)
// The UID whose audio playback becomes active at the last comes first.
// TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
@@ -122,32 +133,24 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
}
final long token = Binder.clearCallingIdentity();
try {
- final Map<Integer, Integer> prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates);
- final Map<Integer, HashSet<Integer>> prevAudioPlayersForUid =
- new HashMap<>(mAudioPlayersForUid);
synchronized (mLock) {
- mAudioPlayerStates.clear();
- mAudioPlayersForUid.clear();
+ // Update mActiveAudioUids
+ mActiveAudioUids.clear();
+ ArrayMap<Integer, AudioPlaybackConfiguration> activeAudioPlaybackConfigs =
+ new ArrayMap<>();
for (AudioPlaybackConfiguration config : configs) {
- int pii = config.getPlayerInterfaceId();
- int uid = config.getClientUid();
- mAudioPlayerStates.put(pii, config.getPlayerState());
- HashSet<Integer> players = mAudioPlayersForUid.get(uid);
- if (players == null) {
- players = new HashSet<Integer>();
- players.add(pii);
- mAudioPlayersForUid.put(uid, players);
- } else {
- players.add(pii);
+ if (config.isActive()) {
+ mActiveAudioUids.add(config.getClientUid());
+ activeAudioPlaybackConfigs.put(config.getPlayerInterfaceId(), config);
}
}
- for (AudioPlaybackConfiguration config : configs) {
- if (!config.isActive()) {
- continue;
- }
- int uid = config.getClientUid();
- if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) {
+ // Update mSortedAuioPlaybackClientUids.
+ for (int i = 0; i < activeAudioPlaybackConfigs.size(); ++i) {
+ AudioPlaybackConfiguration config = activeAudioPlaybackConfigs.valueAt(i);
+ final int uid = config.getClientUid();
+ if (!mPrevActiveAudioPlaybackConfigs.containsKey(
+ config.getPlayerInterfaceId())) {
if (DEBUG) {
Log.d(TAG, "Found a new active media playback. " +
AudioPlaybackConfiguration.toLogFriendlyString(config));
@@ -163,40 +166,21 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
mSortedAudioPlaybackClientUids.add(0, uid);
}
}
- // Notify the change of audio player states.
+ // Notify the active state change of audio players.
for (AudioPlaybackConfiguration config : configs) {
- final Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId());
- final int prevStateInt =
- (prevState == null) ? AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN :
- prevState.intValue();
- if (prevStateInt != config.getPlayerState()) {
- sendAudioPlayerStateChangedMessageLocked(
- config.getClientUid(), prevStateInt, config);
+ final int pii = config.getPlayerInterfaceId();
+ boolean wasActive = mPrevActiveAudioPlaybackConfigs.remove(pii) != null;
+ if (wasActive != config.isActive()) {
+ sendAudioPlayerActiveStateChangedMessageLocked(
+ config, /* isRemoved */ false);
}
}
- for (Integer prevUid : prevAudioPlayersForUid.keySet()) {
- // If all players for prevUid is removed, notify the prev state was
- // PLAYER_STATE_STARTED only when there were a player whose state was
- // PLAYER_STATE_STARTED, otherwise any inactive state is okay to notify.
- if (!mAudioPlayersForUid.containsKey(prevUid)) {
- Set<Integer> prevPlayers = prevAudioPlayersForUid.get(prevUid);
- int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN;
- for (int pii : prevPlayers) {
- Integer state = prevAudioPlayerStates.get(pii);
- if (state == null) {
- continue;
- }
- if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- prevState = state;
- break;
- } else if (prevState
- == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) {
- prevState = state;
- }
- }
- sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null);
- }
+ for (AudioPlaybackConfiguration config : mPrevActiveAudioPlaybackConfigs.values()) {
+ sendAudioPlayerActiveStateChangedMessageLocked(config, /* isRemoved */ true);
}
+
+ // Update mPrevActiveAudioPlaybackConfigs
+ mPrevActiveAudioPlaybackConfigs = activeAudioPlaybackConfigs;
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -204,9 +188,10 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
}
/**
- * Registers OnAudioPlayerStateChangedListener.
+ * Registers OnAudioPlayerActiveStateChangedListener.
*/
- public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) {
+ public void registerListener(
+ OnAudioPlayerActiveStateChangedListener listener, Handler handler) {
synchronized (mLock) {
mListenerMap.put(listener, new MessageHandler((handler == null) ?
Looper.myLooper() : handler.getLooper(), listener));
@@ -214,9 +199,9 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
}
/**
- * Unregisters OnAudioPlayerStateChangedListener.
+ * Unregisters OnAudioPlayerActiveStateChangedListener.
*/
- public void unregisterListener(OnAudioPlayerStateChangedListener listener) {
+ public void unregisterListener(OnAudioPlayerActiveStateChangedListener listener) {
synchronized (mLock) {
mListenerMap.remove(listener);
}
@@ -239,16 +224,7 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
*/
public boolean isPlaybackActive(int uid) {
synchronized (mLock) {
- Set<Integer> players = mAudioPlayersForUid.get(uid);
- if (players == null) {
- return false;
- }
- for (Integer pii : players) {
- if (isActiveState(mAudioPlayerStates.get(pii))) {
- return true;
- }
- }
- return false;
+ return mActiveAudioUids.contains(uid);
}
}
@@ -314,14 +290,10 @@ class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
}
}
- private void sendAudioPlayerStateChangedMessageLocked(
- final int uid, final int prevState, final AudioPlaybackConfiguration config) {
+ private void sendAudioPlayerActiveStateChangedMessageLocked(
+ final AudioPlaybackConfiguration config, final boolean isRemoved) {
for (MessageHandler messageHandler : mListenerMap.values()) {
- messageHandler.sendAudioPlayerStateChangedMessage(uid, prevState, config);
+ messageHandler.sendAudioPlayerActiveStateChangedMessage(config, isRemoved);
}
}
-
- private static boolean isActiveState(Integer state) {
- return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
- }
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3c9e1d456c6f..3e512528e410 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -19,7 +19,7 @@ package com.android.server.media;
import com.android.internal.util.DumpUtils;
import com.android.server.Watchdog;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -101,6 +101,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
private final Handler mHandler = new Handler();
private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
+ private final IntArray mActivePlayerMinPriorityQueue = new IntArray();
+ private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray();
public MediaRouterService(Context context) {
mContext = context;
@@ -111,7 +113,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
mAudioPlayerStateMonitor.registerListener(
- new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
+ new AudioPlayerStateMonitor.OnAudioPlayerActiveStateChangedListener() {
static final long WAIT_MS = 500;
final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() {
@Override
@@ -121,39 +123,41 @@ public final class MediaRouterService extends IMediaRouterService.Stub
};
@Override
- public void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
+ public void onAudioPlayerActiveStateChanged(
+ @NonNull AudioPlaybackConfiguration config, boolean isRemoved) {
+ final boolean active = !isRemoved && config.isActive();
+ final int pii = config.getPlayerInterfaceId();
+ final int uid = config.getClientUid();
+
+ final int idx = mActivePlayerMinPriorityQueue.indexOf(pii);
+ // Keep the latest active player and its uid at the end of the queue.
+ if (idx >= 0) {
+ mActivePlayerMinPriorityQueue.remove(idx);
+ mActivePlayerUidMinPriorityQueue.remove(idx);
+ }
+
int restoreUid = -1;
- boolean active = config == null ? false : config.isActive();
if (active) {
+ mActivePlayerMinPriorityQueue.add(config.getPlayerInterfaceId());
+ mActivePlayerUidMinPriorityQueue.add(uid);
restoreUid = uid;
- } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- // Noting to do if the prev state is not an active state.
- return;
- } else {
- IntArray sortedAudioPlaybackClientUids =
- mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
- for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) {
- if (mAudioPlayerStateMonitor.isPlaybackActive(
- sortedAudioPlaybackClientUids.get(i))) {
- restoreUid = sortedAudioPlaybackClientUids.get(i);
- break;
- }
- }
+ } else if (mActivePlayerUidMinPriorityQueue.size() > 0) {
+ restoreUid = mActivePlayerUidMinPriorityQueue.get(
+ mActivePlayerUidMinPriorityQueue.size() - 1);
}
mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable);
if (restoreUid >= 0) {
restoreRoute(restoreUid);
if (DEBUG) {
- Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
- + " active " + active + " restoring " + restoreUid);
+ Slog.d(TAG, "onAudioPlayerActiveStateChanged: " + "uid=" + uid
+ + ", active=" + active + ", restoreUid=" + restoreUid);
}
} else {
mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS);
if (DEBUG) {
- Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
- + " active " + active + " delaying");
+ Slog.d(TAG, "onAudioPlayerACTIVEStateChanged: " + "uid=" + uid
+ + ", active=" + active + ", delaying");
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f6a81d07277a..06f4f5e8b7c4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,7 +16,6 @@
package com.android.server.media;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.KeyguardManager;
@@ -138,23 +137,19 @@ public class MediaSessionService extends SystemService implements Monitor {
mAudioService = getAudioService();
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
mAudioPlayerStateMonitor.registerListener(
- new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
- @Override
- public void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
- if (config == null || !config.isActive() || config.getPlayerType()
- == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
- return;
- }
- synchronized (mLock) {
- FullUserRecord user =
- getFullUserRecordLocked(UserHandle.getUserId(uid));
- if (user != null) {
- user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+ (config, isRemoved) -> {
+ if (isRemoved || !config.isActive() || config.getPlayerType()
+ == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ return;
}
- }
- }
- }, null /* handler */);
+ synchronized (mLock) {
+ FullUserRecord user = getFullUserRecordLocked(
+ UserHandle.getUserId(config.getClientUid()));
+ if (user != null) {
+ user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+ }
+ }
+ }, null /* handler */);
mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
mContentResolver = getContext().getContentResolver();
mSettingsObserver = new SettingsObserver();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 557ba427e0f6..6ba1d8de79cf 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -736,6 +736,11 @@ public class NotificationManagerService extends SystemService {
for (NotificationVisibility nv : newlyVisibleKeys) {
NotificationRecord r = mNotificationsByKey.get(nv.key);
if (r == null) continue;
+ if (!r.isSeen()) {
+ // Report to usage stats that notification was made visible
+ if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
+ reportSeen(r);
+ }
r.setVisibility(true, nv.rank);
nv.recycle();
}
@@ -766,7 +771,7 @@ public class NotificationManagerService extends SystemService {
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
}
- if (expanded) {
+ if (expanded && userAction) {
r.recordExpanded();
}
EventLogTags.writeNotificationExpansion(key,
@@ -1643,6 +1648,14 @@ public class NotificationManagerService extends SystemService {
return INotificationManager.Stub.asInterface(mService);
}
+ protected void reportSeen(NotificationRecord r) {
+ final int userId = r.sbn.getUserId();
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
+ : userId,
+ UsageEvents.Event.NOTIFICATION_SEEN);
+ }
+
@VisibleForTesting
NotificationManagerInternal getInternalService() {
return mInternalService;
@@ -2269,10 +2282,7 @@ public class NotificationManagerService extends SystemService {
}
if (!r.isSeen()) {
if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
- mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
- : userId,
- UsageEvents.Event.USER_INTERACTION);
+ reportSeen(r);
r.setSeen();
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 679250cb43f3..8591304e512c 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -40,6 +41,7 @@ import com.android.server.PinnerService;
import com.android.server.pm.dex.DexoptOptions;
import java.io.File;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
@@ -402,14 +404,22 @@ public class BackgroundDexOptService extends JobService {
}
/**
- * Execute the idle optimizations immediately.
+ * Execute idle optimizations immediately on packages in packageNames. If packageNames is null,
+ * then execute on all packages.
*/
- public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
+ public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context,
+ @Nullable List<String> packageNames) {
// Create a new object to make sure we don't interfere with the scheduled jobs.
// Note that this may still run at the same time with the job scheduled by the
// JobScheduler but the scheduler will not be able to cancel it.
BackgroundDexOptService bdos = new BackgroundDexOptService();
- int result = bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
+ ArraySet<String> packagesToOptimize;
+ if (packageNames == null) {
+ packagesToOptimize = pm.getOptimizablePackages();
+ } else {
+ packagesToOptimize = new ArraySet<>(packageNames);
+ }
+ int result = bdos.idleOptimization(pm, packagesToOptimize, context);
return result == OPTIMIZE_PROCESSED;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 210eb1385035..6a06be2fcaa9 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -486,6 +486,16 @@ public class Installer extends SystemService {
}
}
+ public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
+ @Nullable String volumeUuid, int flags) throws InstallerException {
+ if (!checkBeforeRemote()) return new byte[0];
+ try {
+ return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
public void invalidateMounts() throws InstallerException {
if (!checkBeforeRemote()) return;
try {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 29f48ee382c6..00cfa3104b0a 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -154,7 +154,13 @@ public class PackageDexOptimizer {
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
final List<String> paths = pkg.getAllCodePaths();
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+
+ int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ if (sharedGid == -1) {
+ Slog.wtf(TAG, "Well this is awkward; package " + pkg.applicationInfo.name + " had UID "
+ + pkg.applicationInfo.uid, new Throwable());
+ sharedGid = android.os.Process.NOBODY_UID;
+ }
// Get the class loader context dependencies.
// For each code path in the package, this array contains the class loader context that
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 83cffe57580e..bbe59eb84904 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -293,6 +293,7 @@ import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
+import com.android.server.pm.dex.DexLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
@@ -2380,7 +2381,10 @@ public class PackageManagerService extends IPackageManager.Stub
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
- mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
+ DexManager.Listener dexManagerListener = DexLogger.getListener(this,
+ installer, mInstallLock);
+ mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
+ dexManagerListener);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -8985,11 +8989,11 @@ public class PackageManagerService extends IPackageManager.Stub
* Execute the background dexopt job immediately.
*/
@Override
- public boolean runBackgroundDexoptJob() {
+ public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
}
- return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext);
+ return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
}
List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 807eb1a8aac4..ee773a515b0f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1219,7 +1219,13 @@ class PackageManagerShellCommand extends ShellCommand {
}
private int runDexoptJob() throws RemoteException {
- boolean result = mInterface.runBackgroundDexoptJob();
+ String arg;
+ List<String> packageNames = new ArrayList<>();
+ while ((arg = getNextArg()) != null) {
+ packageNames.add(arg);
+ }
+ boolean result = mInterface.runBackgroundDexoptJob(packageNames.isEmpty() ? null :
+ packageNames);
return result ? 0 : -1;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1152310116a0..dbf413f0415d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -717,6 +717,19 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ @Override
+ public int getProfileParentId(int userHandle) {
+ checkManageUsersPermission("get the profile parent");
+ synchronized (mUsersLock) {
+ UserInfo profileParent = getProfileParentLU(userHandle);
+ if (profileParent == null) {
+ return userHandle;
+ }
+
+ return profileParent.id;
+ }
+ }
+
private UserInfo getProfileParentLU(int userHandle) {
UserInfo profile = getUserInfoLU(userHandle);
if (profile == null) {
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
new file mode 100644
index 000000000000..c7bbf1cbc5c6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+
+import android.util.ArraySet;
+import android.util.ByteStringUtils;
+import android.util.EventLog;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+
+import java.io.File;
+import java.util.Set;
+
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+
+/**
+ * This class is responsible for logging data about secondary dex files.
+ * The data logged includes hashes of the name and content of each file.
+ */
+public class DexLogger implements DexManager.Listener {
+ private static final String TAG = "DexLogger";
+
+ // Event log tag & subtag used for SafetyNet logging of dynamic
+ // code loading (DCL) - see b/63927552.
+ private static final int SNET_TAG = 0x534e4554;
+ private static final String DCL_SUBTAG = "dcl";
+
+ private final IPackageManager mPackageManager;
+ private final Object mInstallLock;
+ @GuardedBy("mInstallLock")
+ private final Installer mInstaller;
+
+ public static DexManager.Listener getListener(IPackageManager pms,
+ Installer installer, Object installLock) {
+ return new DexLogger(pms, installer, installLock);
+ }
+
+ private DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+ mPackageManager = pms;
+ mInstaller = installer;
+ mInstallLock = installLock;
+ }
+
+ /**
+ * Compute and log hashes of the name and content of a secondary dex file.
+ */
+ @Override
+ public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+ String dexPath, int storageFlags) {
+ int ownerUid = appInfo.uid;
+
+ byte[] hash = null;
+ synchronized(mInstallLock) {
+ try {
+ hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
+ ownerUid, appInfo.volumeUuid, storageFlags);
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
+ " : " + e.getMessage());
+ }
+ }
+ if (hash == null) {
+ return;
+ }
+
+ String dexFileName = new File(dexPath).getName();
+ String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
+ // Valid SHA256 will be 256 bits, 32 bytes.
+ if (hash.length == 32) {
+ message = message + ' ' + ByteStringUtils.toHexString(hash);
+ }
+
+ EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message);
+
+ if (dexUseInfo.isUsedByOtherApps()) {
+ Set<String> otherPackages = dexUseInfo.getLoadingPackages();
+ Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
+ for (String otherPackageName : otherPackages) {
+ try {
+ int otherUid = mPackageManager.getPackageUid(
+ otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
+ if (otherUid != -1 && otherUid != ownerUid) {
+ otherUids.add(otherUid);
+ }
+ } catch (RemoteException ignore) {
+ // Can't happen, we're local.
+ }
+ }
+ for (int otherUid : otherUids) {
+ EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 62747547f320..0e2730cbd944 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -76,6 +76,7 @@ public class DexManager {
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
+ private final Listener mListener;
// Possible outcomes of a dex search.
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
@@ -96,14 +97,24 @@ public class DexManager {
*/
private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
+ public interface Listener {
+ /**
+ * Invoked just before the secondary dex file {@code dexPath} for the specified application
+ * is reconciled.
+ */
+ void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+ String dexPath, int storageFlags);
+ }
+
public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
- Installer installer, Object installLock) {
+ Installer installer, Object installLock, Listener listener) {
mPackageCodeLocationsCache = new HashMap<>();
mPackageDexUsage = new PackageDexUsage();
mPackageManager = pms;
mPackageDexOptimizer = pdo;
mInstaller = installer;
mInstallLock = installLock;
+ mListener = listener;
}
/**
@@ -389,7 +400,7 @@ public class DexManager {
: mPackageDexOptimizer;
String packageName = options.getPackageName();
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
- if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
@@ -433,7 +444,7 @@ public class DexManager {
*/
public void reconcileSecondaryDexFiles(String packageName) {
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
- if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
@@ -481,12 +492,16 @@ public class DexManager {
continue;
}
+ if (mListener != null) {
+ mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
+ }
+
boolean dexStillExists = true;
synchronized(mInstallLock) {
try {
String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
- pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
+ info.uid, isas, info.volumeUuid, flags);
} catch (InstallerException e) {
Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
" : " + e.getMessage());
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9162a97ad5f7..7748ae4f3fe0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -8062,19 +8062,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public boolean canMagnifyWindow(int windowType) {
- switch (windowType) {
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
- case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
- case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
- return false;
- }
- }
- return true;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
&& windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 15121b809f95..3992f8a566ea 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -29,16 +29,21 @@ import android.util.ArrayMap;
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.R;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
/**
* Class to decide whether to turn on battery saver mode for specific service
*
+ * TODO: We should probably make {@link #mFilesForInteractive} and {@link #mFilesForNoninteractive}
+ * less flexible and just take a list of "CPU number - frequency" pairs. Being able to write
+ * anything under /sys/ and /proc/ is too loose.
+ *
* Test: atest BatterySaverPolicyTest
*/
public class BatterySaverPolicy extends ContentObserver {
@@ -62,12 +67,11 @@ public class BatterySaverPolicy extends ContentObserver {
private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
- private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
- private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
+ private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
- private static final String KEY_SCREEN_ON_FILE_PREFIX = "file-on:";
- private static final String KEY_SCREEN_OFF_FILE_PREFIX = "file-off:";
+ private static final String KEY_FILE_FOR_INTERACTIVE_PREFIX = "file-on:";
+ private static final String KEY_FILE_FOR_NONINTERACTIVE_PREFIX = "file-off:";
private static String mSettings;
private static String mDeviceSpecificSettings;
@@ -156,14 +160,9 @@ public class BatterySaverPolicy extends ContentObserver {
private float mAdjustBrightnessFactor;
/**
- * Whether to put all apps in the stand-by mode or not for job scheduler.
- */
- private boolean mForceAllAppsStandbyJobs;
-
- /**
- * Whether to put all apps in the stand-by mode or not for alarms.
+ * Whether to put all apps in the stand-by mode.
*/
- private boolean mForceAllAppsStandbyAlarms;
+ private boolean mForceAllAppsStandby;
/**
* Weather to show non-essential sensors (e.g. edge sensors) or not.
@@ -179,25 +178,25 @@ public class BatterySaverPolicy extends ContentObserver {
private ContentResolver mContentResolver;
@GuardedBy("mLock")
- private final ArrayList<BatterySaverPolicyListener> mListeners = new ArrayList<>();
+ private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
/**
* List of [Filename -> content] that should be written when battery saver is activated
- * and the screen is on.
+ * and the device is interactive.
*
* We use this to change the max CPU frequencies.
*/
@GuardedBy("mLock")
- private ArrayMap<String, String> mScreenOnFiles;
+ private ArrayMap<String, String> mFilesForInteractive;
/**
* List of [Filename -> content] that should be written when battery saver is activated
- * and the screen is off.
+ * and the device is non-interactive.
*
* We use this to change the max CPU frequencies.
*/
@GuardedBy("mLock")
- private ArrayMap<String, String> mScreenOffFiles;
+ private ArrayMap<String, String> mFilesForNoninteractive;
public interface BatterySaverPolicyListener {
void onBatterySaverPolicyChanged(BatterySaverPolicy policy);
@@ -236,11 +235,6 @@ public class BatterySaverPolicy extends ContentObserver {
return R.string.config_batterySaverDeviceSpecificConfig;
}
- @VisibleForTesting
- void onChangeForTest() {
- onChange(true, null);
- }
-
@Override
public void onChange(boolean selfChange, Uri uri) {
final BatterySaverPolicyListener[] listeners;
@@ -297,9 +291,7 @@ public class BatterySaverPolicy extends ContentObserver {
mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
- mForceAllAppsStandbyJobs = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
- mForceAllAppsStandbyAlarms =
- parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
+ mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
// Get default value from Settings.Secure
@@ -315,8 +307,8 @@ public class BatterySaverPolicy extends ContentObserver {
+ deviceSpecificSetting);
}
- mScreenOnFiles = collectParams(parser, KEY_SCREEN_ON_FILE_PREFIX);
- mScreenOffFiles = collectParams(parser, KEY_SCREEN_OFF_FILE_PREFIX);
+ mFilesForInteractive = collectParams(parser, KEY_FILE_FOR_INTERACTIVE_PREFIX);
+ mFilesForNoninteractive = collectParams(parser, KEY_FILE_FOR_NONINTERACTIVE_PREFIX);
}
private static ArrayMap<String, String> collectParams(
@@ -330,7 +322,7 @@ public class BatterySaverPolicy extends ContentObserver {
}
final String path = key.substring(prefix.length());
- if (!(path.startsWith("/sys/") || path.startsWith("/proc"))) {
+ if (!(path.startsWith("/sys/") || path.startsWith("/proc/"))) {
Slog.wtf(TAG, "Invalid path: " + path);
continue;
}
@@ -387,12 +379,6 @@ public class BatterySaverPolicy extends ContentObserver {
case ServiceType.VIBRATION:
return builder.setBatterySaverEnabled(mVibrationDisabled)
.build();
- case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
- return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
- .build();
- case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
- return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
- .build();
case ServiceType.OPTIONAL_SENSORS:
return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
.build();
@@ -403,9 +389,9 @@ public class BatterySaverPolicy extends ContentObserver {
}
}
- public ArrayMap<String, String> getFileValues(boolean screenOn) {
+ public ArrayMap<String, String> getFileValues(boolean interactive) {
synchronized (mLock) {
- return screenOn ? mScreenOnFiles : mScreenOffFiles;
+ return interactive ? mFilesForInteractive : mFilesForNoninteractive;
}
}
@@ -428,17 +414,16 @@ public class BatterySaverPolicy extends ContentObserver {
pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
- pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
- pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
+ pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
pw.println();
- pw.print(" Screen On Files:\n");
- dumpMap(pw, " ", mScreenOnFiles);
+ pw.print(" Interactive File values:\n");
+ dumpMap(pw, " ", mFilesForInteractive);
pw.println();
- pw.print(" Screen Off Files:\n");
- dumpMap(pw, " ", mScreenOffFiles);
+ pw.print(" Noninteractive File values:\n");
+ dumpMap(pw, " ", mFilesForNoninteractive);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a47b8095dae7..584761c3c0ef 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3105,7 +3105,7 @@ public final class PowerManagerService extends SystemService
mIsVrModeEnabled = enabled;
}
- public static void powerHintInternal(int hintId, int data) {
+ private static void powerHintInternal(int hintId, int data) {
nativeSendPowerHint(hintId, data);
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index b3e853838069..3db6a25f5413 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.PowerManagerInternal;
import android.os.PowerManagerInternal.LowPowerModeListener;
import android.os.PowerSaveState;
import android.os.UserHandle;
@@ -33,7 +34,9 @@ import android.util.Slog;
import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
import com.android.server.power.BatterySaverPolicy;
import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener;
import com.android.server.power.PowerManagerService;
@@ -63,20 +66,17 @@ public class BatterySaverController implements BatterySaverPolicyListener {
@GuardedBy("mLock")
private boolean mEnabled;
- /**
- * Keep track of the previous enabled state, which we use to decide when to send broadcasts,
- * which we don't want to send only when the screen state changes.
- */
- @GuardedBy("mLock")
- private boolean mWasEnabled;
-
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case Intent.ACTION_SCREEN_ON:
case Intent.ACTION_SCREEN_OFF:
- mHandler.postStateChanged();
+ if (!isEnabled()) {
+ return; // No need to send it if not enabled.
+ }
+ // Don't send the broadcast, because we never did so in this case.
+ mHandler.postStateChanged(/*sendBroadcast=*/ false);
break;
}
}
@@ -121,25 +121,32 @@ public class BatterySaverController implements BatterySaverPolicyListener {
@Override
public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) {
- mHandler.postStateChanged();
+ if (!isEnabled()) {
+ return; // No need to send it if not enabled.
+ }
+ mHandler.postStateChanged(/*sendBroadcast=*/ true);
}
private class MyHandler extends Handler {
- private final int MSG_STATE_CHANGED = 1;
+ private static final int MSG_STATE_CHANGED = 1;
+
+ private static final int ARG_DONT_SEND_BROADCAST = 0;
+ private static final int ARG_SEND_BROADCAST = 1;
public MyHandler(Looper looper) {
super(looper);
}
- public void postStateChanged() {
- obtainMessage(MSG_STATE_CHANGED).sendToTarget();
+ public void postStateChanged(boolean sendBroadcast) {
+ obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
+ ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
}
@Override
public void dispatchMessage(Message msg) {
switch (msg.what) {
case MSG_STATE_CHANGED:
- handleBatterySaverStateChanged();
+ handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
break;
}
}
@@ -155,38 +162,53 @@ public class BatterySaverController implements BatterySaverPolicyListener {
}
mEnabled = enable;
- mHandler.postStateChanged();
+ mHandler.postStateChanged(/*sendBroadcast=*/ true);
+ }
+ }
+
+ /** @return whether battery saver is enabled or not. */
+ boolean isEnabled() {
+ synchronized (mLock) {
+ return mEnabled;
}
}
/**
* Dispatch power save events to the listeners.
*
- * This is always called on the handler thread.
+ * This method is always called on the handler thread.
+ *
+ * This method is called only in the following cases:
+ * - When battery saver becomes activated.
+ * - When battery saver becomes deactivated.
+ * - When battery saver is on the interactive state changes.
+ * - When battery saver is on the battery saver policy changes.
*/
- void handleBatterySaverStateChanged() {
+ void handleBatterySaverStateChanged(boolean sendBroadcast) {
final LowPowerModeListener[] listeners;
- final boolean wasEnabled;
final boolean enabled;
- final boolean isScreenOn = getPowerManager().isInteractive();
+ final boolean isInteractive = getPowerManager().isInteractive();
final ArrayMap<String, String> fileValues;
synchronized (mLock) {
- Slog.i(TAG, "Battery saver enabled: screen on=" + isScreenOn);
+ Slog.i(TAG, "Battery saver " + (mEnabled ? "enabled" : "disabled")
+ + ": isInteractive=" + isInteractive);
listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
- wasEnabled = mWasEnabled;
enabled = mEnabled;
if (enabled) {
- fileValues = mBatterySaverPolicy.getFileValues(isScreenOn);
+ fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
} else {
fileValues = null;
}
}
- PowerManagerService.powerHintInternal(PowerHint.LOW_POWER, enabled ? 1 : 0);
+ final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+ if (pmi != null) {
+ pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);
+ }
if (enabled) {
// STOPSHIP Remove the toast.
@@ -195,13 +217,13 @@ public class BatterySaverController implements BatterySaverPolicyListener {
Toast.LENGTH_LONG).show();
}
- if (fileValues == null || fileValues.size() == 0) {
+ if (ArrayUtils.isEmpty(fileValues)) {
mFileUpdater.restoreDefault();
} else {
mFileUpdater.writeFiles(fileValues);
}
- if (enabled != wasEnabled) {
+ if (sendBroadcast) {
if (DEBUG) {
Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
}
@@ -231,9 +253,5 @@ public class BatterySaverController implements BatterySaverPolicyListener {
listener.onLowPowerModeChanged(result);
}
}
-
- synchronized (mLock) {
- mWasEnabled = enabled;
- }
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index de4fd7cd06b5..88d1e5560bcc 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -292,6 +292,8 @@ final class AccessibilityController {
public void setMagnificationSpecLocked(MagnificationSpec spec) {
mMagnifedViewport.updateMagnificationSpecLocked(spec);
mMagnifedViewport.recomputeBoundsLocked();
+
+ mService.applyMagnificationSpec(spec);
mService.scheduleAnimationLocked();
}
@@ -421,7 +423,7 @@ final class AccessibilityController {
public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
if (spec != null && !spec.isNop()) {
- if (!mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ if (!windowState.shouldMagnify()) {
return null;
}
}
@@ -476,6 +478,7 @@ final class AccessibilityController {
private final ViewportWindow mWindow;
private boolean mFullRedrawNeeded;
+ private int mTempLayer = 0;
public MagnifiedViewport() {
mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
@@ -565,7 +568,7 @@ final class AccessibilityController {
portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
- if (mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ if (windowState.shouldMagnify()) {
mMagnificationRegion.op(windowBounds, Region.Op.UNION);
mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
} else {
@@ -676,10 +679,12 @@ final class AccessibilityController {
private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+ mTempLayer = 0;
dc.forAllWindows((w) -> {
if (w.isOnScreen() && w.isVisibleLw()
&& !w.mWinAnimator.mEnterAnimationPending) {
- outWindows.put(w.mLayer, w);
+ mTempLayer++;
+ outWindows.put(mTempLayer, w);
}
}, false /* traverseTopToBottom */ );
}
@@ -705,7 +710,7 @@ final class AccessibilityController {
SurfaceControl surfaceControl = null;
try {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+ surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
.setName(SURFACE_TITLE)
.setSize(mTempPoint.x, mTempPoint.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
@@ -714,8 +719,6 @@ final class AccessibilityController {
/* ignore */
}
mSurfaceControl = surfaceControl;
- mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
- .getLayerStack());
mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
TYPE_MAGNIFICATION_OVERLAY)
* WindowManagerService.TYPE_LAYER_MULTIPLIER);
@@ -1005,6 +1008,8 @@ final class AccessibilityController {
private final long mRecurringAccessibilityEventsIntervalMillis;
+ private int mTempLayer = 0;
+
public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
WindowsForAccessibilityCallback callback) {
mContext = windowManagerService.mContext;
@@ -1090,6 +1095,7 @@ final class AccessibilityController {
if (isReportedWindowType(windowState.mAttrs.type)) {
// Add the window to the ones to be reported.
WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
+ window.layer = addedWindows.size();
addedWindows.add(window.token);
windows.add(window);
if (windowState.isFocused()) {
@@ -1323,9 +1329,10 @@ final class AccessibilityController {
private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+ mTempLayer = 0;
dc.forAllWindows((w) -> {
if (w.isVisibleLw()) {
- outWindows.put(w.mLayer, w);
+ outWindows.put(mTempLayer++, w);
}
}, false /* traverseTopToBottom */ );
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2ef7f253f30f..accfc652aac6 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -253,7 +253,6 @@ public class AppWindowAnimator {
private void updateLayers() {
mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
- updateThumbnailLayer();
}
private void stepThumbnailAnimation(long currentTime) {
@@ -283,27 +282,12 @@ public class AppWindowAnimator {
+ "][" + tmpFloats[Matrix.MSKEW_X]
+ "," + tmpFloats[Matrix.MSCALE_Y] + "]");
thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- updateThumbnailLayer();
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
- * Updates the thumbnail layer z order to just above the highest animation layer if changed
- */
- void updateThumbnailLayer() {
- if (thumbnail != null) {
- final int layer = mAppToken.getHighestAnimLayer();
- if (DEBUG_LAYERS) Slog.v(TAG,
- "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
- thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- mThumbnailLayer = layer;
- }
- }
-
- /**
* Sometimes we need to synchronize the first frame of animation with some external event, e.g.
* Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
* and keep producing the first frame of the animation.
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9729e50125b4..f19cd0ff96f7 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -30,7 +30,6 @@ import android.graphics.Rect;
import android.util.Slog;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
/**
* Four black surfaces put together to make a black frame.
@@ -42,22 +41,22 @@ public class BlackFrame {
final int layer;
final SurfaceControl surface;
- BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
- throws OutOfResourcesException {
+ BlackSurface(int layer,
+ int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
left = l;
top = t;
this.layer = layer;
int w = r-l;
int h = b-t;
- surface = new SurfaceControl.Builder(session)
+ surface = dc.makeOverlay()
.setName("BlackSurface")
.setSize(w, h)
.setColorLayer(true)
+ .setParent(null) // TODO: Work-around for b/69259549
.build();
surface.setAlpha(1);
- surface.setLayerStack(layerStack);
surface.setLayer(layer);
surface.show();
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
@@ -114,30 +113,32 @@ public class BlackFrame {
}
}
- public BlackFrame(SurfaceSession session, Rect outer, Rect inner, int layer, int layerStack,
+ public BlackFrame(Rect outer, Rect inner, int layer, DisplayContent dc,
boolean forceDefaultOrientation) throws OutOfResourcesException {
boolean success = false;
mForceDefaultOrientation = forceDefaultOrientation;
+ // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
+ // b/68253229
mOuterRect = new Rect(outer);
mInnerRect = new Rect(inner);
try {
if (outer.top < inner.top) {
- mBlackSurfaces[0] = new BlackSurface(session, layer,
- outer.left, outer.top, inner.right, inner.top, layerStack);
+ mBlackSurfaces[0] = new BlackSurface(layer,
+ outer.left, outer.top, inner.right, inner.top, dc);
}
if (outer.left < inner.left) {
- mBlackSurfaces[1] = new BlackSurface(session, layer,
- outer.left, inner.top, inner.left, outer.bottom, layerStack);
+ mBlackSurfaces[1] = new BlackSurface(layer,
+ outer.left, inner.top, inner.left, outer.bottom, dc);
}
if (outer.bottom > inner.bottom) {
- mBlackSurfaces[2] = new BlackSurface(session, layer,
- inner.left, inner.bottom, outer.right, outer.bottom, layerStack);
+ mBlackSurfaces[2] = new BlackSurface(layer,
+ inner.left, inner.bottom, outer.right, outer.bottom, dc);
}
if (outer.right > inner.right) {
- mBlackSurfaces[3] = new BlackSurface(session, layer,
- inner.right, outer.top, outer.right, inner.bottom, layerStack);
+ mBlackSurfaces[3] = new BlackSurface(layer,
+ inner.right, outer.top, outer.right, inner.bottom, dc);
}
success = true;
} finally {
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2d5d1b2f1da7..2a216abbe4ac 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -33,7 +33,6 @@ import android.view.Display;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
class CircularDisplayMask {
private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM;
@@ -54,8 +53,10 @@ class CircularDisplayMask {
private boolean mDimensionsUnequal = false;
private int mMaskThickness;
- public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
+ public CircularDisplayMask(DisplayContent dc, int zOrder,
int screenOffset, int maskThickness) {
+ final Display display = dc.getDisplay();
+
mScreenSize = new Point();
display.getSize(mScreenSize);
if (mScreenSize.x != mScreenSize.y + screenOffset) {
@@ -66,7 +67,7 @@ class CircularDisplayMask {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("CircularDisplayMask")
.setSize(mScreenSize.x, mScreenSize.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index cc948070f2fb..b534b8ac2244 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -375,6 +375,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return toString();
}
+ boolean isAlwaysOnTop() {
+ return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
+ }
+
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
deleted file mode 100644
index 8fb2be8c256f..000000000000
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ /dev/null
@@ -1,380 +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.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
-
-import java.io.PrintWriter;
-
-public class DimLayer {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
- private final WindowManagerService mService;
-
- /** Actual surface that dims */
- private SurfaceControl mDimSurface;
-
- /** Last value passed to mDimSurface.setAlpha() */
- private float mAlpha = 0;
-
- /** Last value passed to mDimSurface.setLayer() */
- private int mLayer = -1;
-
- /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
- private final Rect mBounds = new Rect();
-
- /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
- private final Rect mLastBounds = new Rect();
-
- /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
- private boolean mShowing = false;
-
- /** Value of mAlpha when beginning transition to mTargetAlpha */
- private float mStartAlpha = 0;
-
- /** Final value of mAlpha following transition */
- private float mTargetAlpha = 0;
-
- /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
- private long mStartTime;
-
- /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
- private long mDuration;
-
- private boolean mDestroyed = false;
-
- private final int mDisplayId;
-
-
- /** Interface implemented by users of the dim layer */
- interface DimLayerUser {
- /** Returns true if the dim should be fullscreen. */
- boolean dimFullscreen();
- /** Returns the display info. of the dim layer user. */
- DisplayInfo getDisplayInfo();
- /** Returns true if the dim layer user is currently attached to a display */
- boolean isAttachedToDisplay();
- /** Gets the bounds of the dim layer user. */
- void getDimBounds(Rect outBounds);
- /** Returns the layer to place a dim layer. */
- default int getLayerForDim(WindowStateAnimator animator, int layerOffset,
- int defaultLayer) {
- return defaultLayer;
- }
-
- String toShortString();
- }
- /** The user of this dim layer. */
- private final DimLayerUser mUser;
-
- private final String mName;
-
- DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
- mUser = user;
- mDisplayId = displayId;
- mService = service;
- mName = name;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
- }
-
- private void constructSurface(WindowManagerService service) {
- service.openSurfaceTransaction();
- try {
- mDimSurface = new SurfaceControl.Builder(service.mFxSession)
- .setName(mName)
- .setSize(16, 16)
- .setColorLayer(true)
- .build();
-
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
- " DIM " + mDimSurface + ": CREATE");
- mDimSurface.setLayerStack(mDisplayId);
- adjustBounds();
- adjustAlpha(mAlpha);
- adjustLayer(mLayer);
- } catch (Exception e) {
- Slog.e(TAG_WM, "Exception creating Dim surface", e);
- } finally {
- service.closeSurfaceTransaction("DimLayer.constructSurface");
- }
- }
-
- /** Return true if dim layer is showing */
- boolean isDimming() {
- return mTargetAlpha != 0;
- }
-
- /** Return true if in a transition period */
- boolean isAnimating() {
- return mTargetAlpha != mAlpha;
- }
-
- float getTargetAlpha() {
- return mTargetAlpha;
- }
-
- void setLayer(int layer) {
- if (mLayer == layer) {
- return;
- }
- mLayer = layer;
- adjustLayer(layer);
- }
-
- private void adjustLayer(int layer) {
- if (mDimSurface != null) {
- mDimSurface.setLayer(layer);
- }
- }
-
- int getLayer() {
- return mLayer;
- }
-
- private void setAlpha(float alpha) {
- if (mAlpha == alpha) {
- return;
- }
- mAlpha = alpha;
- adjustAlpha(alpha);
- }
-
- private void adjustAlpha(float alpha) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
- try {
- if (mDimSurface != null) {
- mDimSurface.setAlpha(alpha);
- }
- if (alpha == 0 && mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
- if (mDimSurface != null) {
- mDimSurface.hide();
- mShowing = false;
- }
- } else if (alpha > 0 && !mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
- if (mDimSurface != null) {
- mDimSurface.show();
- mShowing = true;
- }
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure setting alpha immediately", e);
- }
- }
-
- /**
- * NOTE: Must be called with Surface transaction open.
- */
- private void adjustBounds() {
- if (mUser.dimFullscreen()) {
- getBoundsForFullscreen(mBounds);
- }
-
- if (mDimSurface != null) {
- mDimSurface.setPosition(mBounds.left, mBounds.top);
- mDimSurface.setSize(mBounds.width(), mBounds.height());
- if (DEBUG_DIM_LAYER) Slog.v(TAG,
- "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
- }
-
- mLastBounds.set(mBounds);
- }
-
- private void getBoundsForFullscreen(Rect outBounds) {
- final int dw, dh;
- final float xPos, yPos;
- // Set surface size to screen size.
- final DisplayInfo info = mUser.getDisplayInfo();
- // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
- // a corner.
- dw = (int) (info.logicalWidth * 1.5);
- dh = (int) (info.logicalHeight * 1.5);
- // back off position so 1/4 of Surface is before and 1/4 is after.
- xPos = -1 * dw / 6;
- yPos = -1 * dh / 6;
- outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
- }
-
- void setBoundsForFullscreen() {
- getBoundsForFullscreen(mBounds);
- setBounds(mBounds);
- }
-
- /** @param bounds The new bounds to set */
- void setBounds(Rect bounds) {
- mBounds.set(bounds);
- if (isDimming() && !mLastBounds.equals(bounds)) {
- try {
- mService.openSurfaceTransaction();
- adjustBounds();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure setting size", e);
- } finally {
- mService.closeSurfaceTransaction("DimLayer.setBounds");
- }
- }
- }
-
- /**
- * @param duration The time to test.
- * @return True if the duration would lead to an earlier end to the current animation.
- */
- private boolean durationEndsEarlier(long duration) {
- return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
- }
-
- /** Jump to the end of the animation.
- * NOTE: Must be called with Surface transaction open. */
- void show() {
- if (isAnimating()) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
- show(mLayer, mTargetAlpha, 0);
- }
- }
-
- /**
- * Begin an animation to a new dim value.
- * NOTE: Must be called with Surface transaction open.
- *
- * @param layer The layer to set the surface to.
- * @param alpha The dim value to end at.
- * @param duration How long to take to get there in milliseconds.
- */
- void show(int layer, float alpha, long duration) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
- + " duration=" + duration + ", mDestroyed=" + mDestroyed);
- if (mDestroyed) {
- Slog.e(TAG, "show: no Surface");
- // Make sure isAnimating() returns false.
- mTargetAlpha = mAlpha = 0;
- return;
- }
-
- if (mDimSurface == null) {
- constructSurface(mService);
- }
-
- if (!mLastBounds.equals(mBounds)) {
- adjustBounds();
- }
- setLayer(layer);
-
- long curTime = SystemClock.uptimeMillis();
- final boolean animating = isAnimating();
- if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
- || (!animating && mAlpha != alpha)) {
- if (duration <= 0) {
- // No animation required, just set values.
- setAlpha(alpha);
- } else {
- // Start or continue animation with new parameters.
- mStartAlpha = mAlpha;
- mStartTime = curTime;
- mDuration = duration;
- }
- }
- mTargetAlpha = alpha;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
- + mStartTime + " mTargetAlpha=" + mTargetAlpha);
- }
-
- /** Immediate hide.
- * NOTE: Must be called with Surface transaction open. */
- void hide() {
- if (mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
- hide(0);
- }
- }
-
- /**
- * Gradually fade to transparent.
- * NOTE: Must be called with Surface transaction open.
- *
- * @param duration Time to fade in milliseconds.
- */
- void hide(long duration) {
- if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
- show(mLayer, 0, duration);
- }
- }
-
- /**
- * Advance the dimming per the last #show(int, float, long) call.
- * NOTE: Must be called with Surface transaction open.
- *
- * @return True if animation is still required after this step.
- */
- boolean stepAnimation() {
- if (mDestroyed) {
- Slog.e(TAG, "stepAnimation: surface destroyed");
- // Ensure that isAnimating() returns false;
- mTargetAlpha = mAlpha = 0;
- return false;
- }
- if (isAnimating()) {
- final long curTime = SystemClock.uptimeMillis();
- final float alphaDelta = mTargetAlpha - mStartAlpha;
- float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
- if (alphaDelta > 0 && alpha > mTargetAlpha ||
- alphaDelta < 0 && alpha < mTargetAlpha) {
- // Don't exceed limits.
- alpha = mTargetAlpha;
- }
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
- setAlpha(alpha);
- }
-
- return isAnimating();
- }
-
- /** Cleanup */
- void destroySurface() {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
- if (mDimSurface != null) {
- mDimSurface.destroy();
- mDimSurface = null;
- }
- mDestroyed = true;
- }
-
- public void printTo(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
- pw.print(" mLayer="); pw.print(mLayer);
- pw.print(" mAlpha="); pw.println(mAlpha);
- pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
- pw.print(" mBounds="); pw.println(mBounds.toShortString());
- pw.print(prefix); pw.print("Last animation: ");
- pw.print(" mDuration="); pw.print(mDuration);
- pw.print(" mStartTime="); pw.print(mStartTime);
- pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
- pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
- pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
- }
-}
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
deleted file mode 100644
index 6f9e45a66909..000000000000
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ /dev/null
@@ -1,403 +0,0 @@
-package com.android.server.wm;
-
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
-
-import android.graphics.Rect;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.TypedValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.DimLayer.DimLayerUser;
-
-import java.io.PrintWriter;
-
-/**
- * Centralizes the control of dim layers used for
- * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
- * as well as other use cases (such as dimming above a dead window).
- */
-class DimLayerController {
- private static final String TAG_LOCAL = "DimLayerController";
- private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
- /** Amount of time in milliseconds to animate the dim surface from one value to another,
- * when no window animation is driving it. */
- private static final int DEFAULT_DIM_DURATION = 200;
-
- /**
- * The default amount of dim applied over a dead window
- */
- private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
-
- // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
- // instead of creating a new object per fullscreen task on a display.
- private DimLayer mSharedFullScreenDimLayer;
-
- private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
-
- private DisplayContent mDisplayContent;
-
- private Rect mTmpBounds = new Rect();
-
- DimLayerController(DisplayContent displayContent) {
- mDisplayContent = displayContent;
- }
-
- /** Updates the dim layer bounds, recreating it if needed. */
- void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
- final DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
- final boolean previousFullscreen = state.dimLayer != null
- && state.dimLayer == mSharedFullScreenDimLayer;
- DimLayer newDimLayer;
- final int displayId = mDisplayContent.getDisplayId();
- if (dimLayerUser.dimFullscreen()) {
- if (previousFullscreen && mSharedFullScreenDimLayer != null) {
- // Update the bounds for fullscreen in case of rotation.
- mSharedFullScreenDimLayer.setBoundsForFullscreen();
- return;
- }
- // Use shared fullscreen dim layer
- newDimLayer = mSharedFullScreenDimLayer;
- if (newDimLayer == null) {
- if (state.dimLayer != null) {
- // Re-purpose the previous dim layer.
- newDimLayer = state.dimLayer;
- } else {
- // Create new full screen dim layer.
- newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
- getDimLayerTag(dimLayerUser));
- }
- dimLayerUser.getDimBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- mSharedFullScreenDimLayer = newDimLayer;
- } else if (state.dimLayer != null) {
- state.dimLayer.destroySurface();
- }
- } else {
- newDimLayer = (state.dimLayer == null || previousFullscreen)
- ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
- getDimLayerTag(dimLayerUser))
- : state.dimLayer;
- dimLayerUser.getDimBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- }
- state.dimLayer = newDimLayer;
- }
-
- private static String getDimLayerTag(DimLayerUser dimLayerUser) {
- return TAG_LOCAL + "/" + dimLayerUser.toShortString();
- }
-
- private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
- + dimLayerUser.toShortString());
- DimLayerState state = mState.get(dimLayerUser);
- if (state == null) {
- state = new DimLayerState();
- mState.put(dimLayerUser, state);
- }
- return state;
- }
-
- private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (state == null) {
- if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
- + dimLayerUser.toShortString());
- return;
- }
- state.continueDimming = true;
- }
-
- boolean isDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayerState state = mState.valueAt(i);
- if (state.dimLayer != null && state.dimLayer.isDimming()) {
- return true;
- }
- }
- return false;
- }
-
- void resetDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- mState.valueAt(i).continueDimming = false;
- }
- }
-
- private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- return state != null && state.continueDimming;
- }
-
- void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
- WindowStateAnimator newWinAnimator, boolean aboveApp) {
- // Only set dim params on the highest dimmed layer.
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
- state.dimAbove = aboveApp;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " newWinAnimator=" + newWinAnimator
- + " state.animator=" + state.animator);
- if (newWinAnimator.getShown() && (state.animator == null
- || !state.animator.getShown()
- || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
- state.animator = newWinAnimator;
- if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) {
- // Dim should cover the entire screen for system windows.
- mDisplayContent.getLogicalDisplayRect(mTmpBounds);
- } else {
- dimLayerUser.getDimBounds(mTmpBounds);
- }
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
-
- void stopDimmingIfNeeded() {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
- stopDimmingIfNeeded(dimLayerUser);
- }
- }
-
- private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
- // No need to check if state is null, we know the key has a value.
- DimLayerState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.continueDimming=" + state.continueDimming
- + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
- if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
- return;
- }
-
- if (!state.continueDimming && state.dimLayer.isDimming()) {
- state.animator = null;
- dimLayerUser.getDimBounds(mTmpBounds);
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
-
- boolean animateDimLayers() {
- int fullScreen = -1;
- int fullScreenAndDimming = -1;
- int topFullScreenUserLayer = 0;
- boolean result = false;
-
- for (int i = mState.size() - 1; i >= 0; i--) {
- final DimLayer.DimLayerUser user = mState.keyAt(i);
- final DimLayerState state = mState.valueAt(i);
-
- if (!user.isAttachedToDisplay()) {
- // Leaked dim user that is no longer attached to the display. Go ahead and clean it
- // clean-up and log what happened.
- // TODO: This is a work around for b/34395537 as the dim user should have cleaned-up
- // it self when it was detached from the display. Need to investigate how the dim
- // user is leaking...
- //Slog.wtfStack(TAG_WM, "Leaked dim user=" + user.toShortString()
- // + " state=" + state);
- Slog.w(TAG_WM, "Leaked dim user=" + user.toShortString() + " state=" + state);
- removeDimLayerUser(user);
- continue;
- }
-
- // We have to check that we are actually the shared fullscreen layer
- // for this path. If we began as non fullscreen and became fullscreen
- // (e.g. Docked stack closing), then we may not be the shared layer
- // and we have to make sure we always animate the layer.
- if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
- fullScreen = i;
- if (!state.continueDimming) {
- continue;
- }
-
- // When choosing which user to assign the shared fullscreen layer to
- // we need to look at Z-order.
- if (topFullScreenUserLayer == 0 ||
- (state.animator != null && state.animator.mAnimLayer > topFullScreenUserLayer)) {
- fullScreenAndDimming = i;
- if (state.animator != null) {
- topFullScreenUserLayer = state.animator.mAnimLayer;
- }
- }
- } else {
- // We always want to animate the non fullscreen windows, they don't share their
- // dim layers.
- result |= animateDimLayers(user);
- }
- }
- // For the shared, full screen dim layer, we prefer the animation that is causing it to
- // appear.
- if (fullScreenAndDimming != -1) {
- result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
- } else if (fullScreen != -1) {
- // If there is no animation for the full screen dim layer to appear, we can use any of
- // the animators that will cause it to disappear.
- result |= animateDimLayers(mState.keyAt(fullScreen));
- }
- return result;
- }
-
- private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.animator=" + state.animator
- + " state.continueDimming=" + state.continueDimming);
- final int dimLayer;
- final float dimAmount;
- if (state.animator == null) {
- dimLayer = state.dimLayer.getLayer();
- dimAmount = 0;
- } else {
- if (state.dimAbove) {
- dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
- dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
- } else {
- dimLayer = dimLayerUser.getLayerForDim(state.animator, LAYER_OFFSET_DIM,
- state.animator.mAnimLayer - LAYER_OFFSET_DIM);
- dimAmount = state.animator.mWin.mAttrs.dimAmount;
- }
- }
- final float targetAlpha = state.dimLayer.getTargetAlpha();
- if (targetAlpha != dimAmount) {
- if (state.animator == null) {
- state.dimLayer.hide(DEFAULT_DIM_DURATION);
- } else {
- long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
- ? state.animator.mAnimation.computeDurationHint()
- : DEFAULT_DIM_DURATION;
- if (targetAlpha > dimAmount) {
- duration = getDimLayerFadeDuration(duration);
- }
- state.dimLayer.show(dimLayer, dimAmount, duration);
-
- // If we showed a dim layer, make sure to redo the layout because some things depend
- // on whether a dim layer is showing or not.
- if (targetAlpha == 0) {
- mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
- mDisplayContent.setLayoutNeeded();
- }
- }
- } else if (state.dimLayer.getLayer() != dimLayer) {
- state.dimLayer.setLayer(dimLayer);
- }
- if (state.dimLayer.isAnimating()) {
- if (!mDisplayContent.okToAnimate()) {
- // Jump to the end of the animation.
- state.dimLayer.show();
- } else {
- return state.dimLayer.stepAnimation();
- }
- }
- return false;
- }
-
- boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
- DimLayerState state = mState.get(dimLayerUser);
- return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
- }
-
- private long getDimLayerFadeDuration(long duration) {
- TypedValue tv = new TypedValue();
- mDisplayContent.mService.mContext.getResources().getValue(
- com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
- if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long) tv.getFraction(duration, duration);
- } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
- duration = tv.data;
- }
- return duration;
- }
-
- void close() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayerState state = mState.valueAt(i);
- state.dimLayer.destroySurface();
- }
- mState.clear();
- mSharedFullScreenDimLayer = null;
- }
-
- void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (state != null) {
- // Destroy the surface, unless it's the shared fullscreen dim.
- if (state.dimLayer != mSharedFullScreenDimLayer) {
- state.dimLayer.destroySurface();
- }
- mState.remove(dimLayerUser);
- }
- if (mState.isEmpty()) {
- mSharedFullScreenDimLayer = null;
- }
- }
-
- @VisibleForTesting
- boolean hasDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- return mState.containsKey(dimLayerUser);
- }
-
- @VisibleForTesting
- boolean hasSharedFullScreenDimLayer() {
- return mSharedFullScreenDimLayer != null;
- }
-
- void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
- applyDim(dimLayerUser, animator, false /* aboveApp */);
- }
-
- void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
- applyDim(dimLayerUser, animator, true /* aboveApp */);
- }
-
- void applyDim(
- DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
- if (dimLayerUser == null) {
- Slog.e(TAG, "Trying to apply dim layer for: " + this
- + ", but no dim layer user found.");
- return;
- }
- if (!getContinueDimming(dimLayerUser)) {
- setContinueDimming(dimLayerUser);
- if (!isDimming(dimLayerUser, animator)) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
- startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
- }
- }
- }
-
- private static class DimLayerState {
- // The particular window requesting a dim layer. If null, hide dimLayer.
- WindowStateAnimator animator;
- // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
- // end then stop any dimming.
- boolean continueDimming;
- DimLayer dimLayer;
- boolean dimAbove;
- }
-
- void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DimLayerController");
- final String doubleSpace = " ";
- final String prefixPlusDoubleSpace = prefix + doubleSpace;
-
- for (int i = 0, n = mState.size(); i < n; i++) {
- pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString());
- DimLayerState state = mState.valueAt(i);
- pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer="
- + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer)
- + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming);
- if (state.dimLayer != null) {
- state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw);
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
new file mode 100644
index 000000000000..9fe16ae85cfb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -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
+ */
+
+package com.android.server.wm;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.graphics.Rect;
+
+/**
+ * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
+ * black layers of varying opacity at various Z-levels which create the effect of a Dim.
+ */
+class Dimmer {
+ private static final String TAG = "WindowManager";
+
+ private class DimState {
+ SurfaceControl mSurfaceControl;
+ boolean mDimming;
+
+ /**
+ * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
+ * details on Dim lifecycle.
+ */
+ boolean mDontReset;
+
+ DimState(SurfaceControl ctl) {
+ mSurfaceControl = ctl;
+ mDimming = true;
+ }
+ };
+
+ private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
+
+ /**
+ * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
+ * host, some controller of it, or one of the hosts children.
+ */
+ private WindowContainer mHost;
+
+ Dimmer(WindowContainer host) {
+ mHost = host;
+ }
+
+ SurfaceControl makeDimLayer() {
+ final SurfaceControl control = mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer(true)
+ .setName("Dim Layer for - " + mHost.getName())
+ .build();
+ return control;
+ }
+
+ /**
+ * Retreive the DimState for a given child of the host.
+ */
+ DimState getDimState(WindowContainer container) {
+ DimState state = mDimLayerUsers.get(container);
+ if (state == null) {
+ final SurfaceControl ctl = makeDimLayer();
+ state = new DimState(ctl);
+ /**
+ * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
+ * via state resetting for Dim's with containers.
+ */
+ if (container == null) {
+ state.mDontReset = true;
+ }
+ mDimLayerUsers.put(container, state);
+ }
+ return state;
+ }
+
+ private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
+ float alpha) {
+ final DimState d = getDimState(container);
+ t.show(d.mSurfaceControl);
+ if (container != null) {
+ t.setRelativeLayer(d.mSurfaceControl,
+ container.getSurfaceControl(), relativeLayer);
+ } else {
+ t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
+ }
+ t.setAlpha(d.mSurfaceControl, alpha);
+
+ d.mDimming = true;
+ }
+
+ /**
+ * Finish a dim started by dimAbove in the case there was no call to dimAbove.
+ *
+ * @param t A Transaction in which to finish the dim.
+ */
+ void stopDim(SurfaceControl.Transaction t) {
+ DimState d = getDimState(null);
+ t.hide(d.mSurfaceControl);
+ d.mDontReset = false;
+ }
+ /**
+ * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
+ * remove this effect. If the Dim can be assosciated with a particular child of the host
+ * consider using the other variant of dimAbove which ties the Dim lifetime to the child
+ * lifetime more explicitly.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param alpha The alpha at which to Dim.
+ */
+ void dimAbove(SurfaceControl.Transaction t, float alpha) {
+ dim(t, null, 1, alpha);
+ }
+
+ /**
+ * Place a dim above the given container, which should be a child of the host container.
+ * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
+ * and the child should call dimAbove again to request the Dim to continue.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param container The container which to dim above. Should be a child of our host.
+ * @param alpha The alpha at which to Dim.
+ */
+ void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+ dim(t, container, 1, alpha);
+ }
+
+ /**
+ * Like {@link #dimAbove} but places the dim below the given container.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param container The container which to dim below. Should be a child of our host.
+ * @param alpha The alpha at which to Dim.
+ */
+
+ void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+ dim(t, container, -1, alpha);
+ }
+
+ /**
+ * Mark all dims as pending completion on the next call to {@link #updateDims}
+ *
+ * This is intended for us by the host container, to be called at the beginning of
+ * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
+ * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
+ * a chance to request dims to continue.
+ */
+ void resetDimStates() {
+ for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+ final DimState state = mDimLayerUsers.valueAt(i);
+ if (state.mDontReset == false) {
+ state.mDimming = false;
+ }
+ }
+ }
+
+ /**
+ * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
+ * described in {@link #resetDimStates}.
+ *
+ * @param t A transaction in which to update the dims.
+ * @param bounds The bounds at which to dim.
+ * @return true if any Dims were updated.
+ */
+ boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
+ boolean didSomething = false;
+ for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+ DimState state = mDimLayerUsers.valueAt(i);
+ // TODO: We want to animate the addition and removal of Dim's instead of immediately
+ // acting. When we do this we need to take care to account for the "Replacing Windows"
+ // case (and seamless dim transfer).
+ if (state.mDimming == false) {
+ mDimLayerUsers.removeAt(i);
+ state.mSurfaceControl.destroy();
+ } else {
+ didSomething = true;
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
+ t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
+ }
+ }
+ return didSomething;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4d839d009c56..17312b217c08 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -142,8 +142,10 @@ import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputDevice;
+import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.WindowManagerPolicy;
import com.android.internal.annotations.VisibleForTesting;
@@ -321,8 +323,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final DockedStackDividerController mDividerControllerLocked;
final PinnedStackController mPinnedStackControllerLocked;
- DimLayerController mDimLayerController;
-
final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
private boolean mHaveBootMsg = false;
@@ -346,10 +346,37 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// {@code false} if this display is in the processing of being created.
private boolean mDisplayReady = false;
- private final WindowLayersController mLayersController;
WallpaperController mWallpaperController;
int mInputMethodAnimLayerAdjustment;
+ private final SurfaceSession mSession = new SurfaceSession();
+
+ /**
+ * We organize all top-level Surfaces in to the following layers.
+ * mOverlayLayer contains a few Surfaces which are always on top of others
+ * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay})
+ * {@link #mWindowingLayer} contains everything else.
+ */
+ private SurfaceControl mOverlayLayer;
+
+ /**
+ * See {@link #mOverlayLayer}
+ */
+ private SurfaceControl mWindowingLayer;
+
+ /**
+ * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
+ * <p>
+ * For these surfaces currently we use a surface based on the larger of width or height so we
+ * don't have to resize when rotating the display.
+ */
+ private int mSurfaceSize;
+
+ /**
+ * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
+ */
+ private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
if (winAnimator.hasSurface()) {
@@ -503,9 +530,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return true;
};
- private final Consumer<WindowState> mPrepareWindowSurfaces =
- w -> w.mWinAnimator.prepareSurfaceLocked(true);
-
private final Consumer<WindowState> mPerformLayout = w -> {
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
@@ -558,12 +582,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
w.updateLastInsetValues();
}
- // Window frames may have changed. Update dim layer with the new bounds.
- final Task task = w.getTask();
- if (task != null) {
- mDimLayerController.updateDimLayer(task);
- }
-
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
+ " mContainingFrame=" + w.mContainingFrame
+ " mDisplayFrame=" + w.mDisplayFrame);
@@ -657,8 +675,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- w.applyDimLayerIfNeeded();
-
if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
&& mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
@@ -741,13 +757,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* initialize direct children.
* @param display May not be null.
* @param service You know.
- * @param layersController window layer controller used to assign layer to the windows on this
- * display.
* @param wallpaperController wallpaper windows controller used to adjust the positioning of the
* wallpaper windows in the window list.
*/
DisplayContent(Display display, WindowManagerService service,
- WindowLayersController layersController, WallpaperController wallpaperController) {
+ WallpaperController wallpaperController) {
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -756,7 +770,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDisplay = display;
mDisplayId = display.getDisplayId();
- mLayersController = layersController;
mWallpaperController = wallpaperController;
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
@@ -766,7 +779,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
- mDimLayerController = new DimLayerController(this);
+
+ mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
+ .setSize(mSurfaceSize, mSurfaceSize)
+ .setOpaque(true);
+ mWindowingLayer = b.setName("Display Root").build();
+ mOverlayLayer = b.setName("Display Overlays").build();
+
+ getPendingTransaction().setLayer(mWindowingLayer, 0)
+ .setLayerStack(mWindowingLayer, mDisplayId)
+ .show(mWindowingLayer)
+ .setLayer(mOverlayLayer, 1)
+ .setLayerStack(mOverlayLayer, mDisplayId)
+ .show(mOverlayLayer);
+ getPendingTransaction().apply();
// These are the only direct children we should ever have and they are permanent.
super.addChild(mBelowAppWindowsContainers, null);
@@ -1030,11 +1058,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
setLayoutNeeded();
final int[] anim = new int[2];
- if (isDimming()) {
- anim[0] = anim[1] = 0;
- } else {
- mService.mPolicy.selectRotationAnimationLw(anim);
- }
+ mService.mPolicy.selectRotationAnimationLw(anim);
if (!rotateSeamlessly) {
mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1], this);
@@ -1071,8 +1095,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// it doesn't support hardware OpenGL emulation yet.
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
&& screenRotationAnimation.hasScreenshot()) {
- if (screenRotationAnimation.setRotationInTransaction(
- rotation, mService.mFxSession,
+ if (screenRotationAnimation.setRotationInTransaction(rotation,
MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
mService.scheduleAnimationLocked();
@@ -1907,22 +1930,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- boolean animateDimLayers() {
- return mDimLayerController.animateDimLayers();
- }
-
- private void resetDimming() {
- mDimLayerController.resetDimming();
- }
-
- boolean isDimming() {
- return mDimLayerController.isDimming();
- }
-
- private void stopDimmingIfNeeded() {
- mDimLayerController.stopDimmingIfNeeded();
- }
-
@Override
void removeIfPossible() {
if (isAnimating()) {
@@ -1938,7 +1945,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
try {
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
- mDimLayerController.close();
if (mService.canDispatchPointerEvents()) {
if (mTapDetector != null) {
mService.unregisterPointerEventListener(mTapDetector);
@@ -1947,6 +1953,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mService.unregisterPointerEventListener(mService.mMousePositionTracker);
}
}
+ // The pending transaction won't be applied so we should
+ // just clean up any surfaces pending destruction.
+ onPendingTransactionApplied();
} finally {
mRemovingDisplay = false;
}
@@ -2228,8 +2237,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
token.dump(pw, " ");
}
}
- pw.println();
- mDimLayerController.dump(prefix, pw);
+
pw.println();
// Dump stack references
@@ -2342,10 +2350,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
- mLayersController.assignWindowLayers(this);
+ assignChildLayers(getPendingTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
}
+
+ // We accumlate the layer changes in-to "getPendingTransaction()" but we defer
+ // the application of this transaction until the animation pass triggers
+ // prepareSurfaces. This allows us to synchronize Z-ordering changes with
+ // the hiding and showing of surfaces.
+ scheduleAnimation();
}
// TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -2701,10 +2715,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- void prepareWindowSurfaces() {
- forAllWindows(mPrepareWindowSurfaces, false /* traverseTopToBottom */);
- }
-
boolean inputMethodClientHasFocus(IInputMethodClient client) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
@@ -2846,7 +2856,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
- resetDimming();
mTmpRecoveringMemory = recoveringMemory;
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
@@ -2857,8 +2866,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTmpApplySurfaceChangesTransactionState.preferredModeId,
true /* inTraversal, must call performTraversalInTrans... below */);
- stopDimmingIfNeeded();
-
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
mLastWallpaperVisible = wallpaperVisible;
@@ -3062,13 +3069,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Include this window.
final WindowStateAnimator winAnim = w.mWinAnimator;
- int layer = winAnim.mSurfaceController.getLayer();
- if (mScreenshotApplicationState.maxLayer < layer) {
- mScreenshotApplicationState.maxLayer = layer;
- }
- if (mScreenshotApplicationState.minLayer > layer) {
- mScreenshotApplicationState.minLayer = layer;
- }
// Don't include wallpaper in bounds calculation
if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
@@ -3112,8 +3112,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final WindowState appWin = mScreenshotApplicationState.appWin;
final boolean screenshotReady = mScreenshotApplicationState.screenshotReady;
- final int maxLayer = mScreenshotApplicationState.maxLayer;
- final int minLayer = mScreenshotApplicationState.minLayer;
if (appToken != null && appWin == null) {
// Can't find a window to snapshot.
@@ -3134,11 +3132,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// because we don't want to release the mWindowMap lock until the screenshot is
// taken.
- if (maxLayer == 0) {
- if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
- + ": returning null maxLayer=" + maxLayer);
- return null;
- }
if (!mutableIncludeFullDisplay.value) {
// Constrain frame to the screen size.
@@ -3183,8 +3176,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
convertCropForSurfaceFlinger(crop, rot, dw, dh);
if (DEBUG_SCREENSHOT) {
- Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
- + maxLayer + " appToken=" + appToken);
forAllWindows(w -> {
final WindowSurfaceController controller = w.mWinAnimator.mSurfaceController;
Slog.i(TAG_WM, w + ": " + w.mLayer
@@ -3206,11 +3197,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
SurfaceControl.openTransaction();
SurfaceControl.closeTransactionSync();
- bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
+ // TODO(b/68392460): We should screenshot Task controls directly
+ // but it's difficult at the moment as the Task doesn't have the
+ // correct size set.
+ bitmap = screenshoter.screenshot(crop, width, height, 0, 1,
inRotation, rot);
if (bitmap == null) {
- Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
- + ") to layer " + maxLayer);
+ Slog.w(TAG_WM, "Failed to take screenshot");
return null;
}
}
@@ -3366,6 +3359,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* I.e Activities.
*/
private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
+ /**
+ * A control placed at the appropriate level for transitions to occur.
+ */
+ SurfaceControl mAnimationLayer = null;
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
@@ -3677,6 +3674,50 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// to prevent freezing/unfreezing the display too early.
return mLastOrientation;
}
+
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ final int NORMAL_STACK_STATE = 0;
+ final int BOOSTED_STATE = 1;
+ final int ALWAYS_ON_TOP_STATE = 2;
+
+ // We allow stacks to change visual order from the AM specified order due to
+ // Z-boosting during animations. However we must take care to ensure TaskStacks
+ // which are marked as alwaysOnTop remain that way.
+ int layer = 0;
+ for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+ for (int i = 0; i < mChildren.size(); i++) {
+ final TaskStack s = mChildren.get(i);
+ layer++;
+ if (state == NORMAL_STACK_STATE) {
+ s.assignLayer(t, layer);
+ } else if (state == BOOSTED_STATE && s.needsZBoost()) {
+ s.assignLayer(t, layer);
+ } else if (state == ALWAYS_ON_TOP_STATE &&
+ s.isAlwaysOnTop()) {
+ s.assignLayer(t, layer);
+ }
+ s.assignChildLayers(t);
+ }
+ // The appropriate place for App-Transitions to occur is right
+ // above all other animations but still below things in the Picture-and-Picture
+ // windowing mode.
+ if (state == BOOSTED_STATE && mAnimationLayer != null) {
+ t.setLayer(mAnimationLayer, layer + 1);
+ }
+ }
+ }
+
+ @Override
+ void onParentSet() {
+ super.onParentSet();
+ if (getParent() != null) {
+ mAnimationLayer = makeSurface().build();
+ } else {
+ mAnimationLayer.destroy();
+ mAnimationLayer = null;
+ }
+ }
}
/**
@@ -3760,4 +3801,119 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
boolean useIdentityTransform, int rotation);
}
+
+ SurfaceControl.Builder makeSurface(SurfaceSession s) {
+ return mService.makeSurfaceBuilder(s)
+ .setParent(mWindowingLayer);
+ }
+
+ @Override
+ SurfaceSession getSession() {
+ return mSession;
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ SurfaceSession s = child != null ? child.getSession() : getSession();
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
+ b.setSize(mSurfaceSize, mSurfaceSize);
+
+ if (child == null) {
+ return b;
+ }
+
+ b.setName(child.getName());
+ if (child.isScreenOverlay()) {
+ return b.setParent(mOverlayLayer);
+ } else {
+ return b.setParent(mWindowingLayer);
+ }
+ }
+
+ /**
+ * The makeSurface variants are for use by the window-container
+ * hierarchy. makeOverlay here is a function for various non windowing
+ * overlays like the ScreenRotation screenshot, the Strict Mode Flash
+ * and other potpourii.
+ */
+ SurfaceControl.Builder makeOverlay() {
+ return mService.makeSurfaceBuilder(mSession)
+ .setParent(mOverlayLayer);
+ }
+
+ void applyMagnificationSpec(MagnificationSpec spec) {
+ applyMagnificationSpec(getPendingTransaction(), spec);
+ getPendingTransaction().apply();
+ }
+
+ @Override
+ void onParentSet() {
+ // Since we are the top of the SurfaceControl hierarchy here
+ // we create the root surfaces explicitly rather than chaining
+ // up as the default implementation in onParentSet does. So we
+ // explicitly do NOT call super here.
+ }
+
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ t.setLayer(mOverlayLayer, 1)
+ .setLayer(mWindowingLayer, 0);
+
+ // These are layers as children of "mWindowingLayer"
+ mBelowAppWindowsContainers.assignLayer(t, 0);
+ mTaskStackContainers.assignLayer(t, 1);
+ mAboveAppWindowsContainers.assignLayer(t, 2);
+
+ WindowState imeTarget = mService.mInputMethodTarget;
+ if (imeTarget == null || imeTarget.inSplitScreenWindowingMode()) {
+ // In split-screen windowing mode we can't layer the
+ // IME relative to the IME target because it needs to
+ // go over the docked divider, so instead we place it on top
+ // of everything and use relative layering of windows which need
+ // to go above it (see special logic in WindowState#assignLayer)
+ mImeWindowsContainers.assignLayer(t, 3);
+ } else {
+ t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
+ imeTarget.getSurfaceControl(),
+ // TODO: We need to use an extra level on the app surface to ensure
+ // this is always above SurfaceView but always below attached window.
+ 1);
+ }
+
+ // Above we have assigned layers to our children, now we ask them to assign
+ // layers to their children.
+ mBelowAppWindowsContainers.assignChildLayers(t);
+ mTaskStackContainers.assignChildLayers(t);
+ mAboveAppWindowsContainers.assignChildLayers(t);
+ mImeWindowsContainers.assignChildLayers(t);
+ }
+
+ /**
+ * Here we satisfy an unfortunate special case of the IME in split-screen mode. Imagine
+ * that the IME target is one of the docked applications. We'd like the docked divider to be
+ * above both of the applications, and we'd like the IME to be above the docked divider.
+ * However we need child windows of the applications to be above the IME (Text drag handles).
+ * This is a non-strictly hierarcical layering and we need to break out of the Z ordering
+ * somehow. We do this by relatively ordering children of the target to the IME in cooperation
+ * with {@link #WindowState#assignLayer}
+ */
+ void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
+ t.setRelativeLayer(child.getSurfaceControl(), mImeWindowsContainers.getSurfaceControl(), 1);
+ }
+
+ @Override
+ void destroyAfterPendingTransaction(SurfaceControl surface) {
+ mPendingDestroyingSurfaces.add(surface);
+ }
+
+ /**
+ * Destroys any surfaces that have been put into the pending list with
+ * {@link #destroyAfterTransaction}.
+ */
+ void onPendingTransactionApplied() {
+ for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+ mPendingDestroyingSurfaces.get(i).destroy();
+ }
+ mPendingDestroyingSurfaces.clear();
+ }
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index d79ba897150b..8308417fa141 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -54,7 +54,6 @@ import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
import com.android.server.LocalServices;
-import com.android.server.wm.DimLayer.DimLayerUser;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -62,7 +61,7 @@ import java.io.PrintWriter;
/**
* Keeps information about the docked stack divider.
*/
-public class DockedStackDividerController implements DimLayerUser {
+public class DockedStackDividerController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
@@ -114,7 +113,6 @@ public class DockedStackDividerController implements DimLayerUser {
private boolean mLastVisibility = false;
private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
= new RemoteCallbackList<>();
- private final DimLayer mDimLayer;
private boolean mMinimizedDock;
private int mOriginalDockedSide = DOCKED_INVALID;
@@ -141,13 +139,12 @@ public class DockedStackDividerController implements DimLayerUser {
private boolean mImeHideRequested;
private final Rect mLastDimLayerRect = new Rect();
private float mLastDimLayerAlpha;
+ private TaskStack mDimmedStack;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
final Context context = service.mContext;
- mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
- "DockedStackDim");
mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
context, android.R.interpolator.fast_out_slow_in);
loadDimens();
@@ -463,6 +460,11 @@ public class DockedStackDividerController implements DimLayerUser {
}
mOriginalDockedSide = DOCKED_INVALID;
setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
+
+ if (mDimmedStack != null) {
+ mDimmedStack.stopDimming();
+ mDimmedStack = null;
+ }
}
/**
@@ -564,34 +566,12 @@ public class DockedStackDividerController implements DimLayerUser {
final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
- stack.getDimBounds(mTmpRect);
- if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
- if (!mLastDimLayerRect.equals(mTmpRect) || mLastDimLayerAlpha != alpha) {
- try {
- // TODO: This should use the regular animation transaction - here and below
- mService.openSurfaceTransaction();
- mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
- } finally {
- mService.closeSurfaceTransaction("setResizeDimLayer");
- }
- }
- mLastDimLayerRect.set(mTmpRect);
- mLastDimLayerAlpha = alpha;
- } else {
- visibleAndValid = false;
- }
+ mDimmedStack = stack;
+ stack.dim(alpha);
}
- if (!visibleAndValid) {
- if (mLastDimLayerAlpha != 0f) {
- try {
- mService.openSurfaceTransaction();
- mDimLayer.hide();
- } finally {
- mService.closeSurfaceTransaction("setResizeDimLayer");
- }
- }
- mLastDimLayerAlpha = 0f;
+ if (!visibleAndValid && stack != null) {
+ mDimmedStack = null;
+ stack.stopDimming();
}
}
@@ -829,12 +809,8 @@ public class DockedStackDividerController implements DimLayerUser {
return animateForMinimizedDockedStack(now);
} else if (mAnimatingForIme) {
return animateForIme(now);
- } else {
- if (mDimLayer != null && mDimLayer.isDimming()) {
- mDimLayer.setLayer(getResizeDimLayer());
- }
- return false;
}
+ return false;
}
private boolean animateForIme(long now) {
@@ -942,27 +918,6 @@ public class DockedStackDividerController implements DimLayerUser {
+ (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
}
- @Override
- public boolean dimFullscreen() {
- return false;
- }
-
- @Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mDisplayContent != null;
- }
-
- @Override
- public void getDimBounds(Rect outBounds) {
- // This dim layer user doesn't need this.
- }
-
- @Override
public String toShortString() {
return TAG;
}
@@ -977,10 +932,6 @@ public class DockedStackDividerController implements DimLayerUser {
pw.println(prefix + " mMinimizedDock=" + mMinimizedDock);
pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme);
pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider);
- if (mDimLayer.isDimming()) {
- pw.println(prefix + " Dim layer is dimming: ");
- mDimLayer.printTo(prefix + " ", pw);
- }
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 8bec8d7572a4..fddf6ca2a698 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -49,19 +49,19 @@ class EmulatorDisplayOverlay {
private int mRotation;
private boolean mVisible;
- public EmulatorDisplayOverlay(Context context, Display display, SurfaceSession session,
+ public EmulatorDisplayOverlay(Context context, DisplayContent dc,
int zOrder) {
+ final Display display = dc.getDisplay();
mScreenSize = new Point();
display.getSize(mScreenSize);
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("EmulatorDisplayOverlay")
.setSize(mScreenSize.x, mScreenSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(zOrder);
ctrl.setPosition(0, 0);
ctrl.show();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f541926ea304..43dfccc66920 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -138,7 +138,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
ParcelFileDescriptor mSurfaceTraceFd;
RemoteEventTrace mRemoteEventTrace;
- private final WindowLayersController mLayersController;
final WallpaperController mWallpaperController;
private final Handler mHandler;
@@ -163,7 +162,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
RootWindowContainer(WindowManagerService service) {
mService = service;
mHandler = new MyHandler(service.mH.getLooper());
- mLayersController = new WindowLayersController(mService);
mWallpaperController = new WallpaperController(mService);
}
@@ -231,7 +229,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
}
private DisplayContent createDisplayContent(final Display display) {
- final DisplayContent dc = new DisplayContent(display, mService, mLayersController,
+ final DisplayContent dc = new DisplayContent(display, mService,
mWallpaperController);
final int displayId = display.getDisplayId();
@@ -1103,4 +1101,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
String getName() {
return "ROOT";
}
+
+ @Override
+ void scheduleAnimation() {
+ mService.scheduleAnimationLocked();
+ }
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 3350feae1bc0..70bf15ceb4c3 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -225,7 +225,7 @@ class ScreenRotationAnimation {
}
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
- SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
+ boolean inTransaction, boolean forceDefaultOrientation,
boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
@@ -269,7 +269,7 @@ class ScreenRotationAnimation {
try {
try {
- mSurfaceControl = new SurfaceControl.Builder(session)
+ mSurfaceControl = displayContent.makeOverlay()
.setName("ScreenshotSurface")
.setSize(mWidth, mHeight)
.setSecure(isSecure)
@@ -281,7 +281,6 @@ class ScreenRotationAnimation {
// TODO(multidisplay): we should use the proper display
SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
- mSurfaceControl.setLayerStack(display.getLayerStack());
mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
mSurfaceControl.setAlpha(0);
mSurfaceControl.show();
@@ -370,11 +369,11 @@ class ScreenRotationAnimation {
}
// Must be called while in a transaction.
- public boolean setRotationInTransaction(int rotation, SurfaceSession session,
+ public boolean setRotationInTransaction(int rotation,
long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
setRotationInTransaction(rotation);
if (TWO_PHASE_ANIMATION) {
- return startAnimation(session, maxAnimationDuration, animationScale,
+ return startAnimation(maxAnimationDuration, animationScale,
finalWidth, finalHeight, false, 0, 0);
}
@@ -385,7 +384,7 @@ class ScreenRotationAnimation {
/**
* Returns true if animating.
*/
- private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+ private boolean startAnimation(long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, boolean dismissing,
int exitAnim, int enterAnim) {
if (mSurfaceControl == null) {
@@ -561,8 +560,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
- mCustomBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
+ mCustomBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -601,8 +600,8 @@ class ScreenRotationAnimation {
mOriginalWidth*2, mOriginalHeight*2);
inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
}
- mExitingBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
+ mExitingBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -624,8 +623,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
finalWidth*2, finalHeight*2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mEnteringBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
+ mEnteringBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
@@ -642,7 +641,7 @@ class ScreenRotationAnimation {
/**
* Returns true if animating.
*/
- public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+ public boolean dismiss(long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
if (mSurfaceControl == null) {
@@ -650,7 +649,7 @@ class ScreenRotationAnimation {
return false;
}
if (!mStarted) {
- startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+ startAnimation(maxAnimationDuration, animationScale, finalWidth, finalHeight,
true, exitAnim, enterAnim);
}
if (!mStarted) {
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index eb8ee69646d9..f51a6a921984 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -41,15 +41,14 @@ class StrictModeFlash {
private boolean mDrawNeeded;
private final int mThickness = 20;
- public StrictModeFlash(Display display, SurfaceSession session) {
+ public StrictModeFlash(DisplayContent dc) {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("StrictModeFlash")
.setSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
ctrl.setPosition(0, 0);
ctrl.show();
diff --git a/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
new file mode 100644
index 000000000000..5390e5a1f997
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
@@ -0,0 +1,25 @@
+/*
+ * 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.view.SurfaceSession;
+import android.view.SurfaceControl;
+
+interface SurfaceBuilderFactory {
+ SurfaceControl.Builder make(SurfaceSession s);
+};
+
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 13435d76cd07..f70845e52bb9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -51,7 +51,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.function.Consumer;
-class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerUser {
+class Task extends WindowContainer<AppWindowToken> {
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
// Return value from {@link setBounds} indicating no change was made to the Task bounds.
private static final int BOUNDS_CHANGE_NONE = 0;
@@ -105,6 +105,9 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
// stack moves and we in fact do so when moving from full screen to pinned.
private boolean mPreserveNonFloatingState = false;
+ private Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription,
TaskWindowContainerController controller) {
@@ -188,12 +191,6 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
mDeferRemoval = false;
- // Make sure to remove dim layer user first before removing task its from parent.
- DisplayContent content = getDisplayContent();
- if (content != null) {
- content.mDimLayerController.removeDimLayerUser(this);
- }
-
super.removeImmediately();
}
@@ -237,6 +234,8 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
@Override
void onParentSet() {
+ super.onParentSet();
+
// Update task bounds if needed.
updateDisplayInfo(getDisplayContent());
@@ -312,9 +311,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
mBounds.set(bounds);
mRotation = rotation;
- if (displayContent != null) {
- displayContent.mDimLayerController.updateDimLayer(this);
- }
+
onOverrideConfigurationChanged(overrideConfig);
return boundsChange;
}
@@ -482,7 +479,6 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
}
/** Bounds of the task to be used for dimming, as well as touch related tests. */
- @Override
public void getDimBounds(Rect out) {
final DisplayContent displayContent = mStack.getDisplayContent();
// It doesn't matter if we in particular are part of the resize, since we couldn't have
@@ -634,23 +630,6 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return null;
}
- @Override
- public boolean dimFullscreen() {
- return isFullscreen();
- }
-
- @Override
- public int getLayerForDim(WindowStateAnimator animator, int layerOffset, int defaultLayer) {
- // If the dim layer is for a starting window, move the dim layer back in the z-order behind
- // the lowest activity window to ensure it does not occlude the main window if it is
- // translucent
- final AppWindowToken appToken = animator.mWin.mAppToken;
- if (animator.mAttrType == TYPE_APPLICATION_STARTING && hasChild(appToken) ) {
- return Math.min(defaultLayer, appToken.getLowestAnimLayer() - layerOffset);
- }
- return defaultLayer;
- }
-
boolean isFullscreen() {
if (useCurrentBounds()) {
return mFillsParent;
@@ -661,16 +640,6 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
return true;
}
- @Override
- public DisplayInfo getDisplayInfo() {
- return getDisplayContent().getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return getDisplayContent() != null;
- }
-
void forceWindowsScaleable(boolean force) {
mService.openSurfaceTransaction();
try {
@@ -718,9 +687,18 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
mPreserveNonFloatingState = false;
}
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
@Override
- public String toShortString() {
- return "Task=" + mTaskId;
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getDimBounds(mTmpDimBoundsRect);
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
}
@CallSuper
@@ -757,4 +735,8 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
wtoken.dump(pw, triplePrefix);
}
}
+
+ String toShortString() {
+ return "Task=" + mTaskId;
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 12f6b5a20b47..5d4ba0992f16 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -59,7 +59,7 @@ import com.android.server.wm.WindowManagerService.H;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-class TaskPositioner implements DimLayer.DimLayerUser {
+class TaskPositioner {
private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
private static final String TAG_LOCAL = "TaskPositioner";
private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
@@ -99,9 +99,6 @@ class TaskPositioner implements DimLayer.DimLayerUser {
private WindowPositionerEventReceiver mInputEventReceiver;
private Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
- private DimLayer mDimLayer;
- @CtrlType
- private int mCurrentDimSide;
private Rect mTmpRect = new Rect();
private int mSideMargin;
private int mMinVisibleWidth;
@@ -207,15 +204,6 @@ class TaskPositioner implements DimLayer.DimLayerUser {
mService.mActivityManager.resizeTask(
mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
}
-
- if (mCurrentDimSide != CTRL_NONE) {
- final int createMode = mCurrentDimSide == CTRL_LEFT
- ? 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 */);
- }
} catch(RemoteException e) {}
// Post back to WM to handle clean-ups. We still need the input
@@ -243,7 +231,9 @@ class TaskPositioner implements DimLayer.DimLayerUser {
/**
* @param display The Display that the window being dragged is on.
*/
- void register(Display display) {
+ void register(DisplayContent displayContent) {
+ final Display display = displayContent.getDisplay();
+
if (DEBUG_TASK_POSITIONING) {
Slog.d(TAG, "Registering task positioner");
}
@@ -305,7 +295,6 @@ class TaskPositioner implements DimLayer.DimLayerUser {
}
mService.pauseRotationLocked();
- mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
@@ -336,12 +325,6 @@ class TaskPositioner implements DimLayer.DimLayerUser {
mDragWindowHandle = null;
mDragApplicationHandle = null;
mDisplay = null;
-
- if (mDimLayer != null) {
- mDimLayer.destroySurface();
- mDimLayer = null;
- }
- mCurrentDimSide = CTRL_NONE;
mDragEnded = true;
// Resume rotations after a drag.
@@ -434,7 +417,6 @@ class TaskPositioner implements DimLayer.DimLayerUser {
}
updateWindowDragBounds(nX, nY, mTmpRect);
- updateDimLayerVisibility(nX);
return false;
}
@@ -621,88 +603,6 @@ class TaskPositioner implements DimLayer.DimLayerUser {
"updateWindowDragBounds: " + mWindowDragBounds);
}
- private void updateDimLayerVisibility(int x) {
- @CtrlType
- int dimSide = getDimSide(x);
- if (dimSide == mCurrentDimSide) {
- return;
- }
-
- mCurrentDimSide = dimSide;
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
- mService.openSurfaceTransaction();
- if (mCurrentDimSide == CTRL_NONE) {
- mDimLayer.hide();
- } else {
- showDimLayer();
- }
- mService.closeSurfaceTransaction("updateDimLayerVisibility");
- }
-
- /**
- * Returns the side of the screen the dim layer should be shown.
- * @param x horizontal coordinate used to determine if the dim layer should be shown
- * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
- * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
- * shouldn't be shown.
- */
- private int getDimSide(int x) {
- if (!mTask.mStack.inFreeformWindowingMode()
- || !mTask.mStack.fillsParent()
- || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
- return CTRL_NONE;
- }
-
- mTask.mStack.getDimBounds(mTmpRect);
- if (x - mSideMargin <= mTmpRect.left) {
- return CTRL_LEFT;
- }
- if (x + mSideMargin >= mTmpRect.right) {
- return CTRL_RIGHT;
- }
-
- return CTRL_NONE;
- }
-
- private void showDimLayer() {
- mTask.mStack.getDimBounds(mTmpRect);
- if (mCurrentDimSide == CTRL_LEFT) {
- mTmpRect.right = mTmpRect.centerX();
- } else if (mCurrentDimSide == CTRL_RIGHT) {
- mTmpRect.left = mTmpRect.centerX();
- }
-
- mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
- RESIZING_HINT_DURATION_MS);
- }
-
- @Override /** {@link DimLayer.DimLayerUser} */
- public boolean dimFullscreen() {
- return isFullscreen();
- }
-
- boolean isFullscreen() {
- return false;
- }
-
- @Override /** {@link DimLayer.DimLayerUser} */
- public DisplayInfo getDisplayInfo() {
- return mTask.mStack.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mTask != null && mTask.getDisplayContent() != null;
- }
-
- @Override
- public void getDimBounds(Rect out) {
- // This dim layer user doesn't need this.
- }
-
- @Override
public String toShortString() {
return TAG;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 053fb47023e1..f9062a80a509 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -55,6 +55,7 @@ import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.Surface;
+import android.view.SurfaceControl;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -63,7 +64,7 @@ import com.android.server.EventLogTags;
import java.io.PrintWriter;
-public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
+public class TaskStack extends WindowContainer<Task> implements
BoundsAnimationTarget {
/** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
* restrict IME adjustment so that a min portion of top stack remains visible.*/
@@ -108,8 +109,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
/** Density as of last time {@link #mBounds} was set. */
private int mDensity;
- /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
- private DimLayer mAnimationBackgroundSurface;
+ private SurfaceControl mAnimationBackgroundSurface;
+ private boolean mAnimationBackgroundSurfaceIsShown = false;
/** The particular window with an Animation with non-zero background color. */
private WindowStateAnimator mAnimationBackgroundAnimator;
@@ -149,6 +150,13 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
Rect mPreAnimationBounds = new Rect();
+ private Dimmer mDimmer = new Dimmer(this);
+
+ /**
+ * For {@link #prepareSurfaces}.
+ */
+ final Rect mTmpDimBoundsRect = new Rect();
+
TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
mService = service;
mStackId = stackId;
@@ -245,6 +253,35 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
}
+ private void setAnimationBackgroundBounds(Rect bounds) {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().setSize(mAnimationBackgroundSurface, bounds.width(), bounds.height())
+ .setPosition(mAnimationBackgroundSurface, 0, 0);
+ scheduleAnimation();
+ }
+
+ private void hideAnimationSurface() {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().hide(mAnimationBackgroundSurface);
+ mAnimationBackgroundSurfaceIsShown = false;
+ scheduleAnimation();
+ }
+
+ private void showAnimationSurface(float alpha) {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
+ .setAlpha(mAnimationBackgroundSurface, alpha)
+ .show(mAnimationBackgroundSurface);
+ mAnimationBackgroundSurfaceIsShown = true;
+ scheduleAnimation();
+ }
+
private boolean setBounds(Rect bounds) {
boolean oldFullscreen = mFillsParent;
int rotation = Surface.ROTATION_0;
@@ -267,10 +304,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
return false;
}
- if (mDisplayContent != null) {
- mDisplayContent.mDimLayerController.updateDimLayer(this);
- mAnimationBackgroundSurface.setBounds(bounds);
- }
+ setAnimationBackgroundBounds(bounds);
mBounds.set(bounds);
mRotation = rotation;
@@ -368,7 +402,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
/** Bounds of the stack with other system factors taken into consideration. */
- @Override
public void getDimBounds(Rect out) {
getBounds(out);
}
@@ -700,9 +733,12 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
mDisplayContent = dc;
- mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
- "animation background stackId=" + mStackId);
+
updateBoundsForWindowModeChange();
+ mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
+ .setName("animation background stackId=" + mStackId)
+ .build();
+
super.onDisplayChanged(dc);
}
@@ -914,16 +950,16 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
@Override
void onParentSet() {
+ super.onParentSet();
+
if (getParent() != null || mDisplayContent == null) {
return;
}
- // Looks like the stack was removed from the display. Go ahead and clean things up.
- mDisplayContent.mDimLayerController.removeDimLayerUser(this);
EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
if (mAnimationBackgroundSurface != null) {
- mAnimationBackgroundSurface.destroySurface();
+ mAnimationBackgroundSurface.destroy();
mAnimationBackgroundSurface = null;
}
@@ -933,9 +969,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
void resetAnimationBackgroundAnimator() {
mAnimationBackgroundAnimator = null;
- if (mAnimationBackgroundSurface != null) {
- mAnimationBackgroundSurface.hide();
- }
+ hideAnimationSurface();
}
void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
@@ -944,8 +978,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|| animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
mAnimationBackgroundAnimator = winAnimator;
animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
- mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM,
- ((color >> 24) & 0xff) / 255f, 0);
+ showAnimationSurface(((color >> 24) & 0xff) / 255f);
}
}
@@ -1250,7 +1283,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
proto.write(FILLS_PARENT, mFillsParent);
mBounds.writeToProto(proto, BOUNDS);
- proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurface.isDimming());
+ proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
proto.end(token);
}
@@ -1273,9 +1306,8 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(prefix + " ", pw);
}
- if (mAnimationBackgroundSurface.isDimming()) {
- pw.println(prefix + "mWindowAnimationBackgroundSurface:");
- mAnimationBackgroundSurface.printTo(prefix + " ", pw);
+ if (mAnimationBackgroundSurfaceIsShown) {
+ pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
}
if (!mExitingAppTokens.isEmpty()) {
pw.println();
@@ -1299,11 +1331,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
@Override
- public boolean dimFullscreen() {
- return !isActivityTypeStandard() || fillsParent();
- }
-
- @Override
boolean fillsParent() {
if (useCurrentBounds()) {
return mFillsParent;
@@ -1315,16 +1342,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
@Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mDisplayContent != null;
- }
-
- @Override
public String toString() {
return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
}
@@ -1333,7 +1350,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
return toShortString();
}
- @Override
public String toShortString() {
return "Stack=" + mStackId;
}
@@ -1691,4 +1707,32 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
|| activityType == ACTIVITY_TYPE_RECENTS
|| activityType == ACTIVITY_TYPE_ASSISTANT;
}
+
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getDimBounds(mTmpDimBoundsRect);
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
+
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayContent.getDisplayInfo();
+ }
+
+ void dim(float alpha) {
+ mDimmer.dimAbove(getPendingTransaction(), alpha);
+ scheduleAnimation();
+ }
+
+ void stopDimming() {
+ mDimmer.stopDim(getPendingTransaction());
+ scheduleAnimation();
+ }
}
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index d97aaac4fea5..9216b66e5088 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -53,7 +53,7 @@ class Watermark {
private int mLastDH;
private boolean mDrawNeeded;
- Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) {
+ Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens) {
if (false) {
Log.i(TAG_WM, "*********************** WATERMARK");
for (int i=0; i<tokens.length; i++) {
@@ -61,7 +61,7 @@ class Watermark {
}
}
- mDisplay = display;
+ mDisplay = dc.getDisplay();
mTokens = tokens;
StringBuilder builder = new StringBuilder(32);
@@ -114,7 +114,7 @@ class Watermark {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("WatermarkSurface")
.setSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 1912095caa1d..20bade67047e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -200,7 +200,7 @@ public class WindowAnimator {
++mAnimTransactionSequence;
dc.updateWindowsForAnimator(this);
dc.updateWallpaperForAnimator(this);
- dc.prepareWindowSurfaces();
+ dc.prepareSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
@@ -214,8 +214,6 @@ public class WindowAnimator {
if (screenRotationAnimation != null) {
screenRotationAnimation.updateSurfacesInTransaction();
}
-
- orAnimating(dc.animateDimLayers());
orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
//TODO (multidisplay): Magnification is supported only for the default display.
if (accessibilityController != null && dc.isDefaultDisplay) {
@@ -237,6 +235,13 @@ public class WindowAnimator {
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ dc.onPendingTransactionApplied();
+ }
+
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
boolean doRequest = false;
if (mBulkUpdateParams != 0) {
@@ -271,6 +276,7 @@ public class WindowAnimator {
mService.destroyPreservedSurfaceLocked();
mService.mWindowPlacerLocked.destroyPendingSurfaces();
+
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8f4b897cd804..a5e62884a9fb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -21,9 +21,13 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static android.view.SurfaceControl.Transaction;
import android.annotation.CallSuper;
import android.content.res.Configuration;
+import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.util.Pools;
import android.util.proto.ProtoOutputStream;
@@ -64,7 +68,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
new Pools.SynchronizedPool<>(3);
// The owner/creator for this container. No controller if null.
- private WindowContainerController mController;
+ WindowContainerController mController;
+
+ protected SurfaceControl mSurfaceControl;
+
+ /**
+ * Applied as part of the animation pass in "prepareSurfaces".
+ */
+ private Transaction mPendingTransaction = new Transaction();
@Override
final protected WindowContainer getParent() {
@@ -101,7 +112,22 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* Supposed to be overridden and contain actions that should be executed after parent was set.
*/
void onParentSet() {
- // Do nothing by default.
+ if (mParent == null) {
+ return;
+ }
+ if (mSurfaceControl == null) {
+ // If we don't yet have a surface, but we now have a parent, we should
+ // build a surface.
+ mSurfaceControl = makeSurface().build();
+ getPendingTransaction().show(mSurfaceControl);
+ } else {
+ // If we have a surface but a new parent, we just need to perform a reparent.
+ getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+ }
+
+ // Either way we need to ask the parent to assign us a Z-order.
+ mParent.assignChildLayers();
+ scheduleAnimation();
}
// Temp. holders for a chain of containers we are currently processing.
@@ -188,6 +214,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mChildren.remove(child);
}
+ if (mSurfaceControl != null) {
+ destroyAfterPendingTransaction(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+
if (mParent != null) {
mParent.removeChild(this);
}
@@ -195,6 +226,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (mController != null) {
setController(null);
}
+
}
/**
@@ -407,7 +439,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
-a * Returns whether this child is on top of the window hierarchy.
+ * @return Whether this child is on top of the window hierarchy.
*/
boolean isOnTop() {
return getParent().getTopChild() == this && getParent().isOnTop();
@@ -673,6 +705,103 @@ a * Returns whether this child is on top of the window hierarchy.
mController = controller;
}
+ SurfaceControl.Builder makeSurface() {
+ final WindowContainer p = getParent();
+ return p.makeChildSurface(this);
+ }
+
+ /**
+ * @param child The WindowContainer this child surface is for, or null if the Surface
+ * is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
+ */
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ final WindowContainer p = getParent();
+ // Give the parent a chance to set properties. In hierarchy v1 we rely
+ // on this to set full-screen dimensions on all our Surface-less Layers.
+ final SurfaceControl.Builder b = p.makeChildSurface(child);
+ if (child != null && child.isScreenOverlay()) {
+ // If it's a screen overlay it's been promoted in the hierarchy (wrt to the
+ // WindowContainer hierarchy vs the SurfaceControl hierarchy)
+ // and we shouldn't set ourselves as the parent.
+ return b;
+ } else {
+ return b.setParent(mSurfaceControl);
+ }
+ }
+
+ /**
+ * There are various layers which require promotion from the WindowContainer
+ * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState}
+ * for the particular usage.
+ *
+ * TODO: Perhaps this should be eliminated, either through modifying
+ * the window container hierarchy or through modifying the way we express these overlay
+ * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode
+ * Flash and not actually use a WindowState).
+ */
+ boolean isScreenOverlay() {
+ return false;
+ }
+
+ /**
+ * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
+ */
+ boolean shouldMagnify() {
+ for (int i = 0; i < mChildren.size(); i++) {
+ if (!mChildren.get(i).shouldMagnify()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ SurfaceSession getSession() {
+ if (getParent() != null) {
+ return getParent().getSession();
+ }
+ return null;
+ }
+
+ void assignLayer(Transaction t, int layer) {
+ if (mSurfaceControl != null) {
+ t.setLayer(mSurfaceControl, layer);
+ }
+ }
+
+ void assignChildLayers(Transaction t) {
+ int layer = 0;
+ boolean boosting = false;
+
+ // We use two passes as a way to promote children which
+ // need Z-boosting to the end of the list.
+ for (int i = 0; i < 2; i++ ) {
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowContainer wc = mChildren.get(j);
+ if (wc.needsZBoost() && !boosting) {
+ continue;
+ }
+ wc.assignLayer(t, layer);
+ wc.assignChildLayers(t);
+
+ layer++;
+ }
+ boosting = true;
+ }
+ }
+
+ void assignChildLayers() {
+ assignChildLayers(getPendingTransaction());
+ }
+
+ boolean needsZBoost() {
+ for (int i = 0; i < mChildren.size(); i++) {
+ if (mChildren.get(i).needsZBoost()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Write to a protocol buffer output stream. Protocol buffer message definition is at
* {@link com.android.server.wm.proto.WindowContainerProto}.
@@ -719,4 +848,59 @@ a * Returns whether this child is on top of the window hierarchy.
mConsumerWrapperPool.release(this);
}
}
+
+ // TODO(b/68336570): Should this really be on WindowContainer since it
+ // can only be used on the top-level nodes that aren't animated?
+ // (otherwise we would be fighting other callers of setMatrix).
+ void applyMagnificationSpec(Transaction t, MagnificationSpec spec) {
+ if (shouldMagnify()) {
+ t.setMatrix(mSurfaceControl, spec.scale, 0, 0, spec.scale)
+ .setPosition(mSurfaceControl, spec.offsetX, spec.offsetY);
+ } else {
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).applyMagnificationSpec(t, spec);
+ }
+ }
+ }
+
+ /**
+ * TODO: Once we totally eliminate global transaction we will pass transaction in here
+ * rather than merging to global.
+ */
+ void prepareSurfaces() {
+ SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).prepareSurfaces();
+ }
+ }
+
+ /**
+ * Trigger a call to prepareSurfaces from the animation thread, such that
+ * mPendingTransaction will be applied.
+ */
+ void scheduleAnimation() {
+ if (mParent != null) {
+ mParent.scheduleAnimation();
+ }
+ }
+
+ SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
+ /**
+ * Destroy a given surface after executing mPendingTransaction. This is
+ * largely a workaround for destroy not being part of transactions
+ * rather than an intentional design, so please take care when
+ * expanding use.
+ */
+ void destroyAfterPendingTransaction(SurfaceControl surface) {
+ if (mParent != null) {
+ mParent.destroyAfterPendingTransaction(surface);
+ }
+ }
+
+ Transaction getPendingTransaction() {
+ return mPendingTransaction;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
deleted file mode 100644
index 7caf2fe9b149..000000000000
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.util.Slog;
-
-import java.util.ArrayDeque;
-import java.util.function.Consumer;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
-import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
-
-/**
- * Controller for assigning layers to windows on the display.
- *
- * This class encapsulates general algorithm for assigning layers and special rules that we need to
- * apply on top. The general algorithm goes through windows from bottom to the top and the higher
- * the window is, the higher layer is assigned. The final layer is equal to base layer +
- * adjustment from the order. This means that the window list is assumed to be ordered roughly by
- * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
- * handled with care, because they break the algorithm).
- *
- * On top of the general algorithm we add special rules, that govern such amazing things as:
- * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
- * <li>docked/pinned windows (that need to be lifted above other application windows, including
- * animations)
- * <li>dock divider (which needs to live above applications, but below IME)</li>
- * <li>replaced windows, which need to live above their normal level, because they anticipate
- * an animation</li>.
- */
-class WindowLayersController {
- private final WindowManagerService mService;
-
- WindowLayersController(WindowManagerService service) {
- mService = service;
- }
-
- private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
- private WindowState mDockDivider = null;
- private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
- private int mCurBaseLayer;
- private int mCurLayer;
- private boolean mAnyLayerChanged;
- private int mHighestApplicationLayer;
- private int mHighestDockedAffectedLayer;
- private int mHighestLayerInImeTargetBaseLayer;
- private WindowState mImeTarget;
- private boolean mAboveImeTarget;
- private ArrayDeque<WindowState> mAboveImeTargetAppWindows = new ArrayDeque();
-
- private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
- boolean layerChanged = false;
-
- int oldLayer = w.mLayer;
- if (w.mBaseLayer == mCurBaseLayer) {
- mCurLayer += WINDOW_LAYER_MULTIPLIER;
- } else {
- mCurBaseLayer = mCurLayer = w.mBaseLayer;
- }
- assignAnimLayer(w, mCurLayer);
-
- // TODO: Preserved old behavior of code here but not sure comparing oldLayer to
- // mAnimLayer and mLayer makes sense...though the worst case would be unintentional
- // layer reassignment.
- if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
- layerChanged = true;
- mAnyLayerChanged = true;
- }
-
- if (w.mAppToken != null) {
- mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
- w.mWinAnimator.mAnimLayer);
- }
- if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
- mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
- w.mWinAnimator.mAnimLayer);
- }
- if (w.getAppToken() != null && w.inSplitScreenSecondaryWindowingMode()) {
- mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
- w.mWinAnimator.mAnimLayer);
- }
-
- collectSpecialWindows(w);
-
- if (layerChanged) {
- w.scheduleAnimationIfDimming();
- }
- };
-
- final void assignWindowLayers(DisplayContent dc) {
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based",
- new RuntimeException("here").fillInStackTrace());
-
- reset();
- dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
-
- adjustSpecialWindows();
-
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && mAnyLayerChanged
- && dc.getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onWindowLayersChangedLocked();
- }
-
- if (DEBUG_LAYERS) logDebugLayers(dc);
- }
-
- private void logDebugLayers(DisplayContent dc) {
- dc.forAllWindows((w) -> {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
- + " mLayer=" + w.mLayer + (w.mAppToken == null
- ? "" : " mAppLayer=" + w.mAppToken.getAnimLayerAdjustment())
- + " =mAnimLayer=" + winAnimator.mAnimLayer);
- }, false /* traverseTopToBottom */);
- }
-
- private void reset() {
- mPinnedWindows.clear();
- mInputMethodWindows.clear();
- mDockedWindows.clear();
- mAssistantWindows.clear();
- mReplacingWindows.clear();
- mDockDivider = null;
-
- mCurBaseLayer = 0;
- mCurLayer = 0;
- mAnyLayerChanged = false;
-
- mHighestApplicationLayer = 0;
- mHighestDockedAffectedLayer = 0;
- mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
- mImeTarget = mService.mInputMethodTarget;
- mAboveImeTarget = false;
- mAboveImeTargetAppWindows.clear();
- }
-
- private void collectSpecialWindows(WindowState w) {
- if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
- mDockDivider = w;
- return;
- }
- if (w.mWillReplaceWindow) {
- mReplacingWindows.add(w);
- }
- if (w.mIsImWindow) {
- mInputMethodWindows.add(w);
- return;
- }
- if (mImeTarget != null) {
- if (w.getParentWindow() == mImeTarget && w.mSubLayer > 0) {
- // Child windows of the ime target with a positive sub-layer should be placed above
- // the IME.
- mAboveImeTargetAppWindows.add(w);
- } else if (mAboveImeTarget && w.mAppToken != null) {
- // windows of apps above the IME target should be placed above the IME.
- mAboveImeTargetAppWindows.add(w);
- }
- if (w == mImeTarget) {
- mAboveImeTarget = true;
- }
- }
-
- final int windowingMode = w.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_PINNED) {
- mPinnedWindows.add(w);
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mDockedWindows.add(w);
- }
- if (w.isActivityTypeAssistant()) {
- mAssistantWindows.add(w);
- }
- }
-
- private void adjustSpecialWindows() {
- // The following adjustments are beyond the highest docked-affected layer
- int layer = mHighestDockedAffectedLayer + TYPE_LAYER_OFFSET;
-
- // Adjust the docked stack windows and dock divider above only the windows that are affected
- // by the docked stack. When this happens, also boost the assistant window layers, otherwise
- // the docked stack windows & divider would be promoted above the assistant.
- if (!mDockedWindows.isEmpty() && mHighestDockedAffectedLayer > 0) {
- while (!mDockedWindows.isEmpty()) {
- final WindowState window = mDockedWindows.remove();
- layer = assignAndIncreaseLayerIfNeeded(window, layer);
- }
-
- layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
-
- while (!mAssistantWindows.isEmpty()) {
- final WindowState window = mAssistantWindows.remove();
- if (window.mLayer > mHighestDockedAffectedLayer) {
- layer = assignAndIncreaseLayerIfNeeded(window, layer);
- }
- }
- }
-
- // The following adjustments are beyond the highest app layer or boosted layer
- layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER);
-
- // We know that we will be animating a relaunching window in the near future, which will
- // receive a z-order increase. We want the replaced window to immediately receive the same
- // treatment, e.g. to be above the dock divider.
- while (!mReplacingWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
- }
-
- while (!mPinnedWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
- }
-
- // Make sure IME is the highest window in the base layer of it's target.
- if (mImeTarget != null) {
- if (mImeTarget.mAppToken == null) {
- // For non-app ime targets adjust the layer we start from to match what we found
- // when assigning layers. Otherwise, just use the highest app layer we have some far.
- layer = mHighestLayerInImeTargetBaseLayer + WINDOW_LAYER_MULTIPLIER;
- }
-
- while (!mInputMethodWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
- }
-
- // Adjust app windows the should be displayed above the IME since they are above the IME
- // target.
- while (!mAboveImeTargetAppWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mAboveImeTargetAppWindows.remove(), layer);
- }
- }
-
- }
-
- private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
- if (win != null) {
- assignAnimLayer(win, layer);
- // Make sure we leave space in-between normal windows for dims and such.
- layer += WINDOW_LAYER_MULTIPLIER;
- }
- return layer;
- }
-
- private void assignAnimLayer(WindowState w, int layer) {
- w.mLayer = layer;
- w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
- + w.getSpecialWindowAnimLayerAdjustment();
- if (w.mAppToken != null) {
- w.mAppToken.mAppAnimator.updateThumbnailLayer();
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ce3430615df6..465653938672 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -529,7 +529,6 @@ public class WindowManagerService extends IWindowManager.Stub
AccessibilityController mAccessibilityController;
- final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
CircularDisplayMask mCircularDisplayMask;
@@ -804,6 +803,13 @@ public class WindowManagerService extends IWindowManager.Stub
static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
new WindowManagerThreadPriorityBooster();
+ class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory {
+ public SurfaceControl.Builder make(SurfaceSession s) {
+ return new SurfaceControl.Builder(s);
+ }
+ };
+ SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory();
+
static void boostPriorityForLockedSection() {
sThreadPriorityBooster.boost();
}
@@ -992,7 +998,6 @@ public class WindowManagerService extends IWindowManager.Stub
mPointerEventDispatcher = null;
}
- mFxSession = new SurfaceSession();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mDisplays = mDisplayManager.getDisplays();
for (Display display : mDisplays) {
@@ -2478,11 +2483,8 @@ public class WindowManagerService extends IWindowManager.Stub
mWaitingForConfig = true;
displayContent.setLayoutNeeded();
int anim[] = new int[2];
- if (displayContent.isDimming()) {
- anim[0] = anim[1] = 0;
- } else {
- mPolicy.selectRotationAnimationLw(anim);
- }
+ mPolicy.selectRotationAnimationLw(anim);
+
startFreezingDisplayLocked(false, anim[0], anim[1], displayContent);
config = new Configuration(mTempConfiguration);
}
@@ -3592,8 +3594,7 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.dimen.circular_display_mask_thickness);
mCircularDisplayMask = new CircularDisplayMask(
- getDefaultDisplayContentLocked().getDisplay(),
- mFxSession,
+ getDefaultDisplayContentLocked(),
mPolicy.getWindowLayerFromTypeLw(
WindowManager.LayoutParams.TYPE_POINTER)
* TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
@@ -3621,8 +3622,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mEmulatorDisplayOverlay == null) {
mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
mContext,
- getDefaultDisplayContentLocked().getDisplay(),
- mFxSession,
+ getDefaultDisplayContentLocked(),
mPolicy.getWindowLayerFromTypeLw(
WindowManager.LayoutParams.TYPE_POINTER)
* TYPE_LAYER_MULTIPLIER + 10);
@@ -3670,7 +3670,7 @@ public class WindowManagerService extends IWindowManager.Stub
// TODO(multi-display): support multiple displays
if (mStrictModeFlash == null) {
mStrictModeFlash = new StrictModeFlash(
- getDefaultDisplayContentLocked().getDisplay(), mFxSession);
+ getDefaultDisplayContentLocked());
}
mStrictModeFlash.setVisibility(on);
} finally {
@@ -4522,7 +4522,7 @@ public class WindowManagerService extends IWindowManager.Stub
Display display = displayContent.getDisplay();
mTaskPositioner = new TaskPositioner(this);
- mTaskPositioner.register(display);
+ mTaskPositioner.register(displayContent);
mInputMonitor.updateInputWindowsLw(true /*force*/);
// We need to grab the touch focus so that the touch events during the
@@ -5892,7 +5892,7 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
+ inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
@@ -5952,11 +5952,10 @@ public class WindowManagerService extends IWindowManager.Stub
// TODO(multidisplay): rotation on main screen only.
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
- boolean isDimming = displayContent.isDimming();
- if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
+ if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
mExitAnimId = mEnterAnimId = 0;
}
- if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+ if (screenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
scheduleAnimationLocked();
@@ -6040,8 +6039,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (toks != null && toks.length > 0) {
// TODO(multi-display): Show watermarks on secondary displays.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- mWatermark = new Watermark(displayContent.getDisplay(),
- displayContent.mRealDisplayMetrics, mFxSession, toks);
+ mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
+ toks);
}
}
} catch (FileNotFoundException e) {
@@ -7557,4 +7556,13 @@ public class WindowManagerService extends IWindowManager.Stub
w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
}, false /* traverseTopToBottom */);
}
+
+ public void applyMagnificationSpec(MagnificationSpec spec) {
+ getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+ }
+
+ SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
+ return mSurfaceBuilderFactory.make(s);
+ }
}
+
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6b1932d7b6ae..52b7a25684f1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -23,6 +23,7 @@ import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -41,6 +42,7 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -54,6 +56,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
@@ -147,6 +152,8 @@ import android.view.IWindowId;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowInfo;
@@ -602,6 +609,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
};
};
+ /**
+ * Indicates whether we have requested a Dim (in the sense of {@link Dimmer}) from our host
+ * container.
+ */
+ private boolean mIsDimming = false;
+
+ private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
@@ -2034,23 +2049,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return isVisibleOrAdding();
}
- void scheduleAnimationIfDimming() {
- final DisplayContent dc = getDisplayContent();
- if (dc == null) {
- return;
- }
-
- // If layout is currently deferred, we want to hold of with updating the layers.
- if (mService.mWindowPlacerLocked.isLayoutDeferred()) {
- return;
- }
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- if (dimLayerUser != null && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator)) {
- // Force an animation pass just to update the mDimLayer layer.
- mService.scheduleAnimationLocked();
- }
- }
-
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
super(inputChannel, mService.mH.getLooper());
@@ -2106,31 +2104,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mInputWindowHandle.inputChannel = null;
}
- void applyDimLayerIfNeeded() {
- // When the app is terminated (eg. from Recents), the task might have already been
- // removed with the window pending removal. Don't apply dim in such cases, as there
- // will be no more updateDimLayer() calls, which leaves the dimlayer invalid.
- final AppWindowToken token = mAppToken;
- if (token != null && token.removed) {
- return;
- }
-
- final DisplayContent dc = getDisplayContent();
- if (!mAnimatingExit && mAppDied) {
- // If app died visible, apply a dim over the window to indicate that it's inactive
- dc.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
- } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
- && dc != null && !mAnimatingExit && isVisible()) {
- dc.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
- }
- }
-
- private DimLayer.DimLayerUser getDimLayerUser() {
+ private Dimmer getDimmer() {
Task task = getTask();
if (task != null) {
- return task;
+ return task.getDimmer();
}
- return getStack();
+ return getStack().getDimmer();
}
/** Returns true if the replacement window was removed. */
@@ -2152,9 +2131,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private void removeReplacedWindow() {
if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
- if (isDimming()) {
- transferDimToReplacement();
- }
mWillReplaceWindow = false;
mAnimateReplacingWindow = false;
mReplacingRemoveRequested = false;
@@ -2217,11 +2193,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// need to intercept touches outside of that window. The dim layer user
// associated with the window (task or stack) will give us the good bounds, as
// they would be used to display the dim layer.
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- if (dimLayerUser != null) {
- dimLayerUser.getDimBounds(mTmpRect);
+ final Task task = getTask();
+ if (task != null) {
+ task.getDimBounds(mTmpRect);
} else {
- getVisibleBounds(mTmpRect);
+ getStack().getDimBounds(mTmpRect);
}
if (inFreeformWindowingMode()) {
// For freeform windows we the touch region to include the whole surface for the
@@ -2729,14 +2705,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return displayContent.isDefaultDisplay;
}
- @Override
- public boolean isDimming() {
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- final DisplayContent dc = getDisplayContent();
- return dimLayerUser != null && dc != null
- && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
- }
-
void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
mShowToOwnerOnly = showToOwnerOnly;
}
@@ -3071,7 +3039,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (task == null) {
return false;
}
- if (!inSplitScreenWindowingMode()) {
+ if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
return false;
}
if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
@@ -3591,15 +3559,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return winY;
}
- private void transferDimToReplacement() {
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- final DisplayContent dc = getDisplayContent();
- if (dimLayerUser != null && dc != null) {
- dc.mDimLayerController.applyDim(dimLayerUser,
- mReplacementWindow.mWinAnimator, (mAttrs.flags & FLAG_DIM_BEHIND) != 0);
- }
- }
-
// During activity relaunch due to resize, we sometimes use window replacement
// for only child windows (as the main window is handled by window preservation)
// and the big surface.
@@ -4388,4 +4347,80 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
}
+
+ @Override
+ boolean shouldMagnify() {
+ if (mAttrs.type == TYPE_INPUT_METHOD ||
+ mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+ return false;
+ } else if (isScreenOverlay()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ boolean isScreenOverlay() {
+ // It's tempting to wonder: Have we forgotten the rounded corners overlay?
+ // worry not: it's a fake TYPE_NAVIGATION_BAR.
+ if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
+ mAttrs.type == TYPE_NAVIGATION_BAR ||
+ mAttrs.type == TYPE_STATUS_BAR) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ SurfaceSession getSession() {
+ if (mSession.mSurfaceSession != null) {
+ return mSession.mSurfaceSession;
+ } else {
+ return getParent().getSession();
+ }
+ }
+
+ @Override
+ boolean needsZBoost() {
+ return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
+ }
+
+ @Override
+ SurfaceControl.Builder makeSurface() {
+ return mToken.makeChildSurface(this);
+ }
+
+
+ @Override
+ void prepareSurfaces() {
+ mIsDimming = false;
+ if (!mAnimatingExit && mAppDied) {
+ mIsDimming = true;
+ getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
+ } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+ && !mAnimatingExit && isVisible()) {
+ mIsDimming = true;
+ getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
+ }
+
+ mWinAnimator.prepareSurfaceLocked(true);
+ super.prepareSurfaces();
+ }
+
+ @Override
+ void assignLayer(Transaction t, int layer) {
+ // See comment in assignRelativeLayerForImeTargetChild
+ if (!isChildWindow()
+ || (mService.mInputMethodTarget != getParentWindow())
+ || !inSplitScreenWindowingMode()) {
+ super.assignLayer(t, layer);
+ return;
+ }
+ getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+ }
+
+ @Override
+ public boolean isDimming() {
+ return mIsDimming;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 86397aea6a17..840cc40860ff 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -60,7 +60,6 @@ import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
-import android.view.MagnificationSpec;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -559,7 +558,10 @@ class WindowStateAnimator {
}
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
if (mSurfaceController != null) {
- mSurfaceController.setLayer(mAnimLayer + 1);
+ // Our SurfaceControl is always at layer 0 within the parent Surface managed by
+ // window-state. We want this old Surface to stay on top of the new one
+ // until we do the swap, so we place it at layer 1.
+ mSurfaceController.mSurfaceControl.setLayer(1);
}
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
@@ -730,7 +732,6 @@ class WindowStateAnimator {
try {
mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
mSurfaceController.setLayerStackInTransaction(getLayerStack());
- mSurfaceController.setLayer(mAnimLayer);
} finally {
mService.closeSurfaceTransaction("createSurfaceLocked");
}
@@ -867,22 +868,6 @@ class WindowStateAnimator {
mPendingDestroySurface = null;
}
- void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
- final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
- final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
-
- if (spec != null && !spec.isNop()) {
- float scale = spec.scale;
- transform.postScale(scale, scale);
- transform.postTranslate(spec.offsetX, spec.offsetY);
-
- // As we are scaling the whole surface, to keep the content
- // in the same position we will also have to scale the surfaceInsets.
- transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
- -(surfaceInsetTop*scale - surfaceInsetTop));
- }
- }
-
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;
Transformation attachedTransformation =
@@ -969,11 +954,6 @@ class WindowStateAnimator {
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null) {
- applyMagnificationSpec(spec, tmpMatrix);
- }
-
// "convert" it into SurfaceFlinger's format
// (a 2x2 matrix + an offset)
// Here we must not transform the position of the surface
@@ -1057,49 +1037,16 @@ class WindowStateAnimator {
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null) {
- final Rect frame = mWin.mFrame;
- final float tmpFloats[] = mService.mTmpFloats;
- final Matrix tmpMatrix = mWin.mTmpMatrix;
-
- tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
- tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
-
- applyMagnificationSpec(spec, tmpMatrix);
-
- tmpMatrix.getValues(tmpFloats);
-
- mHaveMatrix = true;
- mDsDx = tmpFloats[Matrix.MSCALE_X];
- mDtDx = tmpFloats[Matrix.MSKEW_Y];
- mDtDy = tmpFloats[Matrix.MSKEW_X];
- mDsDy = tmpFloats[Matrix.MSCALE_Y];
- float x = tmpFloats[Matrix.MTRANS_X];
- float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set(Math.round(x), Math.round(y));
-
- mShownAlpha = mAlpha;
- } else {
- mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
- if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
- mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
- }
- mShownAlpha = mAlpha;
- mHaveMatrix = false;
- mDsDx = mWin.mGlobalScale;
- mDtDx = 0;
- mDtDy = 0;
- mDsDy = mWin.mGlobalScale;
- }
- }
-
- private MagnificationSpec getMagnificationSpec() {
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && mWin.getDisplayId() == DEFAULT_DISPLAY) {
- return mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
+ mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
+ if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+ mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
}
- return null;
+ mShownAlpha = mAlpha;
+ mHaveMatrix = false;
+ mDsDx = mWin.mGlobalScale;
+ mDtDx = 0;
+ mDtDy = 0;
+ mDsDy = mWin.mGlobalScale;
}
/**
@@ -1140,26 +1087,6 @@ class WindowStateAnimator {
w.expandForSurfaceInsets(finalClipRect);
}
- // We may be applying a magnification spec to all windows,
- // simulating a transformation in screen space, in which case
- // we need to transform all other screen space values...including
- // the final crop. This is kind of messed up and we should look
- // in to actually transforming screen-space via a parent-layer.
- // b/38322835
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null && !spec.isNop()) {
- Matrix transform = mWin.mTmpMatrix;
- RectF finalCrop = mService.mTmpRectF;
- transform.reset();
- transform.postScale(spec.scale, spec.scale);
- transform.postTranslate(-spec.offsetX, -spec.offsetY);
- transform.mapRect(finalCrop);
- finalClipRect.top = (int) finalCrop.top;
- finalClipRect.left = (int) finalCrop.left;
- finalClipRect.right = (int) finalCrop.right;
- finalClipRect.bottom = (int) finalCrop.bottom;
- }
-
return true;
}
@@ -1517,7 +1444,6 @@ class WindowStateAnimator {
mReportSurfaceResized = true;
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- w.applyDimLayerIfNeeded();
}
}
@@ -1615,7 +1541,6 @@ class WindowStateAnimator {
mDtDy * w.mHScale * mExtraHScale,
mDsDy * w.mVScale * mExtraVScale,
recoveringMemory);
- mSurfaceController.setLayer(mAnimLayer);
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index a2145230f956..6746754b92c1 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -53,7 +53,7 @@ class WindowSurfaceController {
final WindowStateAnimator mAnimator;
- private SurfaceControlWithBackground mSurfaceControl;
+ SurfaceControlWithBackground mSurfaceControl;
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
@@ -101,7 +101,8 @@ class WindowSurfaceController {
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
- final SurfaceControl.Builder b = new SurfaceControl.Builder(s)
+ final SurfaceControl.Builder b = win.makeSurface()
+ .setParent(win.getSurfaceControl())
.setName(name)
.setSize(w, h)
.setFormat(format)
@@ -245,25 +246,6 @@ class WindowSurfaceController {
}
}
- void setLayer(int layer) {
- if (mSurfaceControl != null) {
- mService.openSurfaceTransaction();
- try {
- if (mAnimator.mWin.usesRelativeZOrdering()) {
- mSurfaceControl.setRelativeLayer(
- mAnimator.mWin.getParentWindow()
- .mWinAnimator.mSurfaceController.mSurfaceControl,
- -1);
- } else {
- mSurfaceLayer = layer;
- mSurfaceControl.setLayer(layer);
- }
- } finally {
- mService.closeSurfaceTransaction("setLayer");
- }
- }
- }
-
void setLayerStackInTransaction(int layerStack) {
if (mSurfaceControl != null) {
mSurfaceControl.setLayerStack(layerStack);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index cd5e4750554c..508186868c1c 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -704,7 +704,7 @@ class WindowSurfacePlacer {
// Create a new surface for the thumbnail
WindowState window = appToken.findMainWindow();
- final SurfaceControl surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+ final SurfaceControl surfaceControl = appToken.makeSurface()
.setName("thumbnail anim")
.setSize(dirty.width(), dirty.height())
.setFormat(PixelFormat.TRANSLUCENT)
@@ -712,7 +712,6 @@ class WindowSurfacePlacer {
window != null ? window.mOwnerUid : Binder.getCallingUid())
.build();
- surfaceControl.setLayerStack(display.getLayerStack());
if (SHOW_TRANSACTIONS) {
Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE");
}
@@ -750,10 +749,13 @@ class WindowSurfacePlacer {
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
- openingAppAnimator.updateThumbnailLayer();
openingAppAnimator.thumbnail = surfaceControl;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+
+ // We parent the thumbnail to the app token, and just place it
+ // on top of anything else in the app token.
+ surfaceControl.setLayer(Integer.MAX_VALUE);
} catch (Surface.OutOfResourcesException e) {
Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
+ dirty.width() + " h=" + dirty.height(), e);
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 5657f6c4f9c5..6aa51016ebe9 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -67,6 +67,7 @@ class WindowTracing {
mWriteQueue.clear();
mTraceFile.delete();
try (OutputStream os = new FileOutputStream(mTraceFile)) {
+ mTraceFile.setReadable(true, false);
ProtoOutputStream proto = new ProtoOutputStream(os);
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
proto.flush();
@@ -147,7 +148,7 @@ class WindowTracing {
}
static WindowTracing createDefaultAndStartLooper(Context context) {
- File file = new File("/data/system/window_trace.proto");
+ File file = new File("/data/misc/wmtrace/wm_trace.pb");
WindowTracing windowTracing = new WindowTracing(file);
new Thread(windowTracing::loop, "window_tracing").start();
return windowTracing;
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 36e9e7f9215f..4a2da37bcc60 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -23,7 +23,6 @@ LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
- $(LOCAL_REL_DIR)/com_android_server_location_ContextHubService.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_locksettings_SyntheticPasswordManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index 6403a5a0f35a..df53feeaf8fa 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -51,8 +51,9 @@ using V1_0::Band;
using V1_0::BandConfig;
using V1_0::MetaData;
using V1_0::Result;
-using V1_2::ITunerCallback;
using V1_1::ProgramListResult;
+using V1_1::VendorKeyValue;
+using V1_2::ITunerCallback;
using utils::HalRevision;
static mutex gContextMutex;
@@ -93,6 +94,7 @@ struct TunerContext {
wp<V1_1::IBroadcastRadio> mHalModule11;
sp<V1_0::ITuner> mHalTuner;
sp<V1_1::ITuner> mHalTuner11;
+ sp<V1_2::ITuner> mHalTuner12;
sp<HalDeathRecipient> mHalDeathRecipient;
private:
@@ -179,8 +181,11 @@ void assignHalInterfaces(JNIEnv *env, JavaRef<jobject> const &jTuner,
ctx.mHalTuner = halTuner;
ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
+ ctx.mHalTuner12 = V1_2::ITuner::castFrom(halTuner).withDefault(nullptr);
ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr,
"Provided tuner does not implement 1.1 HAL");
+ ALOGW_IF(ctx.mHalRev >= HalRevision::V1_2 && ctx.mHalTuner12 == nullptr,
+ "Provided tuner does not implement 1.2 HAL");
ctx.mHalDeathRecipient = new HalDeathRecipient(getNativeCallback(env, jTuner));
halTuner->linkToDeath(ctx.mHalDeathRecipient, 0);
@@ -194,16 +199,21 @@ static sp<V1_0::ITuner> getHalTuner(const TunerContext& ctx) {
return tuner;
}
-sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
+static sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
lock_guard<mutex> lk(gContextMutex);
return getHalTuner(getNativeContext(nativeContext));
}
-sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
+static sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
lock_guard<mutex> lk(gContextMutex);
return getNativeContext(nativeContext).mHalTuner11;
}
+static sp<V1_2::ITuner> getHalTuner12(jlong nativeContext) {
+ lock_guard<mutex> lk(gContextMutex);
+ return getNativeContext(nativeContext).mHalTuner12;
+}
+
sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) {
return TunerCallback::getNativeCallback(env,
env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback));
@@ -233,6 +243,7 @@ static void nativeClose(JNIEnv *env, jobject obj, jlong nativeContext) {
ctx.mHalDeathRecipient = nullptr;
ctx.mHalTuner11 = nullptr;
+ ctx.mHalTuner12 = nullptr;
ctx.mHalTuner = nullptr;
}
@@ -488,6 +499,48 @@ static void nativeSetAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext,
convert::ThrowIfFailed(env, halResult);
}
+static jobject nativeSetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jParameters) {
+ ALOGV("%s", __func__);
+
+ auto halTuner = getHalTuner12(nativeContext);
+ if (halTuner == nullptr) {
+ ALOGI("Parameters are not supported with HAL < 1.2");
+ return nullptr;
+ }
+
+ JavaRef<jobject> jResults = nullptr;
+ auto parameters = convert::VendorInfoToHal(env, jParameters);
+ auto hidlResult = halTuner->setParameters(parameters,
+ [&](const hidl_vec<VendorKeyValue> results) {
+ jResults = convert::VendorInfoFromHal(env, results);
+ });
+
+ if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+ return jResults.release();
+}
+
+static jobject nativeGetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jKeys) {
+ ALOGV("%s", __func__);
+
+ auto halTuner = getHalTuner12(nativeContext);
+ if (halTuner == nullptr) {
+ ALOGI("Parameters are not supported with HAL < 1.2");
+ return nullptr;
+ }
+
+ JavaRef<jobject> jResults = nullptr;
+ auto keys = convert::StringListToHal(env, jKeys);
+ auto hidlResult = halTuner->getParameters(keys,
+ [&](const hidl_vec<VendorKeyValue> parameters) {
+ jResults = convert::VendorInfoFromHal(env, parameters);
+ });
+
+ if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+ return jResults.release();
+}
+
static bool nativeIsAntennaConnected(JNIEnv *env, jobject obj, jlong nativeContext) {
ALOGV("%s", __func__);
auto halTuner = getHalTuner(nativeContext);
@@ -525,6 +578,8 @@ static const JNINativeMethod gTunerMethods[] = {
{ "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
{ "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
{ "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced },
+ { "nativeSetParameters", "(JLjava/util/Map;)Ljava/util/Map;", (void*)nativeSetParameters },
+ { "nativeGetParameters", "(JLjava/util/List;)Ljava/util/Map;", (void*)nativeGetParameters },
{ "nativeIsAntennaConnected", "(J)Z", (void*)nativeIsAntennaConnected },
};
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index ed7c9c4749c8..d624df69a7ff 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -70,6 +70,7 @@ static struct {
jmethodID onBackgroundScanAvailabilityChange;
jmethodID onBackgroundScanComplete;
jmethodID onProgramListChanged;
+ jmethodID onParametersUpdated;
} TunerCallback;
} gjni;
@@ -346,7 +347,10 @@ Return<void> NativeCallback::currentProgramInfoChanged(const ProgramInfo& info)
Return<void> NativeCallback::parametersUpdated(const hidl_vec<VendorKeyValue>& parameters) {
ALOGV("%s", __func__);
- // TODO(b/65862441): pass this callback to the front-end
+ mCallbackThread.enqueue([this, parameters](JNIEnv *env) {
+ auto jParameters = convert::VendorInfoFromHal(env, parameters);
+ env->CallVoidMethod(mJCallback, gjni.TunerCallback.onParametersUpdated, jParameters.get());
+ });
return {};
}
@@ -437,6 +441,8 @@ void register_android_server_broadcastradio_TunerCallback(JavaVM *vm, JNIEnv *en
"onBackgroundScanComplete", "()V");
gjni.TunerCallback.onProgramListChanged = GetMethodIDOrDie(env, tunerCbClass,
"onProgramListChanged", "()V");
+ gjni.TunerCallback.onParametersUpdated = GetMethodIDOrDie(env, tunerCbClass,
+ "onParametersUpdated", "(Ljava/util/Map;)V");
auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/TunerCallback",
gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 8dfa14f126f7..734ce793e833 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -34,6 +34,7 @@ namespace convert {
namespace utils = hardware::broadcastradio::utils;
using hardware::Return;
+using hardware::hidl_string;
using hardware::hidl_vec;
using regions::RegionalBandConfig;
@@ -98,6 +99,11 @@ static struct {
} HashMap;
struct {
+ jmethodID get;
+ jmethodID size;
+ } List;
+
+ struct {
jmethodID put;
} Map;
@@ -145,8 +151,21 @@ static struct {
jclass clazz;
jmethodID cstor;
} ParcelableException;
+
+ struct {
+ jclass clazz;
+ } String;
} gjni;
+static jstring CastToString(JNIEnv *env, jobject obj) {
+ if (env->IsInstanceOf(obj, gjni.String.clazz)) {
+ return static_cast<jstring>(obj);
+ } else {
+ ALOGE("Cast failed, object is not a string");
+ return nullptr;
+ }
+}
+
template <>
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
return __ThrowIfFailedHidl(env, hidlResult);
@@ -250,12 +269,26 @@ static JavaRef<jobjectArray> ArrayFromHal(JNIEnv *env, const hidl_vec<T>& vec,
}
static std::string StringFromJava(JNIEnv *env, JavaRef<jstring> &jStr) {
- auto cstr = (jStr == nullptr) ? nullptr : env->GetStringUTFChars(jStr.get(), nullptr);
+ if (jStr == nullptr) return {};
+ auto cstr = env->GetStringUTFChars(jStr.get(), nullptr);
std::string str(cstr);
env->ReleaseStringUTFChars(jStr.get(), cstr);
return str;
}
+hidl_vec<hidl_string> StringListToHal(JNIEnv *env, jobject jList) {
+ auto len = (jList == nullptr) ? 0 : env->CallIntMethod(jList, gjni.List.size);
+ hidl_vec<hidl_string> list(len);
+
+ for (decltype(len) i = 0; i < len; i++) {
+ auto jString = make_javaref(env, CastToString(env, env->CallObjectMethod(
+ jList, gjni.List.get, i)));
+ list[i] = StringFromJava(env, jString);
+ }
+
+ return list;
+}
+
JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hidl_vec<VendorKeyValue> &info) {
ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
@@ -275,7 +308,10 @@ hidl_vec<VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo) {
auto jInfoArr = make_javaref(env, static_cast<jobjectArray>(env->CallStaticObjectMethod(
gjni.Convert.clazz, gjni.Convert.stringMapToNative, jInfo)));
- LOG_FATAL_IF(jInfoArr == nullptr, "Converted array is null");
+ if (jInfoArr == nullptr) {
+ ALOGE("Converted array is null");
+ return {};
+ }
auto len = env->GetArrayLength(jInfoArr.get());
hidl_vec<VendorKeyValue> vec;
@@ -651,6 +687,10 @@ void register_android_server_broadcastradio_convert(JNIEnv *env) {
gjni.HashMap.clazz = MakeGlobalRefOrDie(env, hashMapClass);
gjni.HashMap.cstor = GetMethodIDOrDie(env, hashMapClass, "<init>", "()V");
+ auto listClass = FindClassOrDie(env, "java/util/List");
+ gjni.List.get = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+ gjni.List.size = GetMethodIDOrDie(env, listClass, "size", "()I");
+
auto mapClass = FindClassOrDie(env, "java/util/Map");
gjni.Map.put = GetMethodIDOrDie(env, mapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
@@ -713,6 +753,9 @@ void register_android_server_broadcastradio_convert(JNIEnv *env) {
gjni.ParcelableException.clazz = MakeGlobalRefOrDie(env, parcelableExcClass);
gjni.ParcelableException.cstor = GetMethodIDOrDie(env, parcelableExcClass, "<init>",
"(Ljava/lang/Throwable;)V");
+
+ auto stringClass = FindClassOrDie(env, "java/lang/String");
+ gjni.String.clazz = MakeGlobalRefOrDie(env, stringClass);
}
} // namespace android
diff --git a/services/core/jni/BroadcastRadio/convert.h b/services/core/jni/BroadcastRadio/convert.h
index 1fc75f06f38d..b8c55c1f0c62 100644
--- a/services/core/jni/BroadcastRadio/convert.h
+++ b/services/core/jni/BroadcastRadio/convert.h
@@ -35,6 +35,8 @@ namespace convert {
namespace V1_0 = hardware::broadcastradio::V1_0;
namespace V1_1 = hardware::broadcastradio::V1_1;
+hardware::hidl_vec<hardware::hidl_string> StringListToHal(JNIEnv *env, jobject jList);
+
JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hardware::hidl_vec<V1_1::VendorKeyValue> &info);
hardware::hidl_vec<V1_1::VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo);
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
deleted file mode 100644
index ad372deb1c36..000000000000
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ /dev/null
@@ -1,1201 +0,0 @@
-/*
- * Copyright 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_NDEBUG
-#undef LOG_TAG
-#define LOG_NDEBUG 0
-#define LOG_TAG "ContextHubService"
-
-#include <inttypes.h>
-#include <jni.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/endian.h>
-
-#include <chrono>
-#include <mutex>
-#include <queue>
-#include <unordered_map>
-#include <utility>
-
-#include <android-base/macros.h>
-#include <android/hardware/contexthub/1.0/IContexthub.h>
-#include <cutils/log.h>
-
-#include "core_jni_helpers.h"
-#include <nativehelper/JNIHelp.h>
-
-using android::hardware::contexthub::V1_0::AsyncEventType;
-using android::hardware::contexthub::V1_0::ContextHub;
-using android::hardware::contexthub::V1_0::ContextHubMsg;
-using android::hardware::contexthub::V1_0::HubAppInfo;
-using android::hardware::contexthub::V1_0::IContexthub;
-using android::hardware::contexthub::V1_0::IContexthubCallback;
-using android::hardware::contexthub::V1_0::NanoAppBinary;
-using android::hardware::contexthub::V1_0::Result;
-using android::hardware::contexthub::V1_0::TransactionResult;
-
-using android::hardware::Return;
-
-using std::chrono::steady_clock;
-
-// If a transaction takes longer than this, we'll allow it to be
-// canceled by a new transaction. Note we do _not_ automatically
-// cancel a transaction after this much time. We can have a
-// legal transaction which takes longer than this amount of time,
-// as long as no other new transactions are attempted after this
-// time has expired.
-constexpr auto kMinTransactionCancelTime = std::chrono::seconds(29);
-
-namespace android {
-
-constexpr uint32_t kNanoAppBinaryHeaderVersion = 1;
-
-// Important: this header is explicitly defined as little endian byte order, and
-// therefore may not match host endianness
-struct NanoAppBinaryHeader {
- uint32_t headerVersion; // 0x1 for this version
- uint32_t magic; // "NANO" (see NANOAPP_MAGIC in context_hub.h)
- uint64_t appId; // App Id, contains vendor id
- uint32_t appVersion; // Version of the app
- uint32_t flags; // Signed, encrypted
- uint64_t hwHubType; // Which hub type is this compiled for
- uint8_t targetChreApiMajorVersion; // Which CHRE API version this is compiled for
- uint8_t targetChreApiMinorVersion;
- uint8_t reserved[6];
-} __attribute__((packed));
-
-enum HubMessageType {
- CONTEXT_HUB_APPS_ENABLE = 1, // Enables loaded nano-app(s)
- CONTEXT_HUB_APPS_DISABLE = 2, // Disables loaded nano-app(s)
- CONTEXT_HUB_LOAD_APP = 3, // Load a supplied app
- CONTEXT_HUB_UNLOAD_APP = 4, // Unload a specified app
- CONTEXT_HUB_QUERY_APPS = 5, // Query for app(s) info on hub
- CONTEXT_HUB_QUERY_MEMORY = 6, // Query for memory info
- CONTEXT_HUB_OS_REBOOT = 7, // Request to reboot context HUB OS
-};
-
-constexpr jint OS_APP_ID = -1;
-constexpr jint INVALID_APP_ID = -2;
-
-constexpr jint MIN_APP_ID = 1;
-constexpr jint MAX_APP_ID = 128;
-
-constexpr size_t MSG_HEADER_SIZE = 4;
-constexpr size_t HEADER_FIELD_MSG_TYPE = 0;
-constexpr size_t HEADER_FIELD_MSG_VERSION = 1;
-constexpr size_t HEADER_FIELD_HUB_HANDLE = 2;
-constexpr size_t HEADER_FIELD_APP_INSTANCE = 3;
-
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
-constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2;
-
-jint getAppInstanceForAppId(uint64_t app_id);
-int onMessageReceipt(const uint32_t *header,
- size_t headerLen,
- const char *msg,
- size_t msgLen);
-void onHubReset(uint32_t hubId);
-void queryHubForApps(uint32_t hubId);
-void passOnOsResponse(uint32_t hubHandle,
- uint32_t msgType,
- TransactionResult result,
- const int8_t *additionalData,
- size_t additionalDataLen);
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle);
-void closeUnloadTxn(bool success);
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
- uint32_t hubHandle);
-
-struct JniInfo {
- JavaVM *vm;
- jclass contextHubInfoClass;
- jclass contextHubServiceClass;
- jclass memoryRegionsClass;
-
- jobject jContextHubService;
-
- jmethodID msgReceiptCallBack;
-
- jmethodID contextHubInfoCtor;
- jmethodID contextHubInfoSetId;
- jmethodID contextHubInfoSetName;
- jmethodID contextHubInfoSetVendor;
- jmethodID contextHubInfoSetToolchain;
- jmethodID contextHubInfoSetPlatformVersion;
- jmethodID contextHubInfoSetStaticSwVersion;
- jmethodID contextHubInfoSetToolchainVersion;
- jmethodID contextHubInfoSetPeakMips;
- jmethodID contextHubInfoSetStoppedPowerDrawMw;
- jmethodID contextHubInfoSetSleepPowerDrawMw;
- jmethodID contextHubInfoSetPeakPowerDrawMw;
- jmethodID contextHubInfoSetSupportedSensors;
- jmethodID contextHubInfoSetMemoryRegions;
- jmethodID contextHubInfoSetMaxPacketLenBytes;
-
- jmethodID contextHubServiceMsgReceiptCallback;
- jmethodID contextHubServiceAddAppInstance;
- jmethodID contextHubServiceDeleteAppInstance;
-};
-
-
-
-class TxnManager {
-public:
- TxnManager() {
- mData = nullptr;
- mIsPending = false;
- }
-
- ~TxnManager() {
- closeTxn();
- }
-
- int addTxn(HubMessageType txnIdentifier, void *txnData) {
- std::lock_guard<std::mutex>lock(mLock);
- if (mIsPending) {
- ALOGW("Transaction already found pending when trying to add a new one.");
- return -1;
- }
- mIsPending = true;
- mFirstTimeTxnCanBeCanceled = steady_clock::now() + kMinTransactionCancelTime;
- mData = txnData;
- mIdentifier = txnIdentifier;
-
- return 0;
- }
-
- int closeTxn() {
- std::lock_guard<std::mutex>lock(mLock);
- closeTxnUnlocked();
- return 0;
- }
-
- bool isTxnPending() {
- std::lock_guard<std::mutex>lock(mLock);
- return mIsPending;
- }
-
- void closeAnyStaleTxns() {
- std::lock_guard<std::mutex>lock(mLock);
- if (mIsPending && steady_clock::now() >= mFirstTimeTxnCanBeCanceled) {
- ALOGW("Stale transaction canceled");
- closeTxnUnlocked();
- }
- }
-
- int fetchTxnData(HubMessageType *id, void **data) {
- if (id == nullptr || data == nullptr) {
- ALOGW("Null Params isNull{id, data} {%d, %d}",
- id == nullptr ? 1 : 0,
- data == nullptr ? 1 : 0);
- return -1;
- }
-
- std::lock_guard<std::mutex>lock(mLock);
- if (!mIsPending) {
- ALOGW("No Transactions pending");
- return -1;
- }
-
- *id = mIdentifier;
- *data = mData;
- return 0;
- }
-
- private:
- bool mIsPending; // Is a transaction pending
- std::mutex mLock; // mutex for manager
- HubMessageType mIdentifier; // What are we doing
- void *mData; // Details
- steady_clock::time_point mFirstTimeTxnCanBeCanceled;
-
- // Only call this if you hold the lock.
- void closeTxnUnlocked() {
- mIsPending = false;
- free(mData);
- mData = nullptr;
- }
-};
-
-
-struct ContextHubServiceCallback : IContexthubCallback {
- uint32_t mContextHubId;
-
- ContextHubServiceCallback(uint32_t hubId) {
- mContextHubId = hubId;
- }
-
- virtual Return<void> handleClientMsg(const ContextHubMsg &msg) {
- jint appHandle = getAppInstanceForAppId(msg.appName);
- if (appHandle < 0) {
- ALOGE("Filtering out message due to invalid App Instance.");
- } else {
- uint32_t msgHeader[MSG_HEADER_SIZE] = {};
- msgHeader[HEADER_FIELD_MSG_TYPE] = msg.msgType;
- msgHeader[HEADER_FIELD_HUB_HANDLE] = mContextHubId;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = appHandle;
- onMessageReceipt(msgHeader,
- MSG_HEADER_SIZE,
- reinterpret_cast<const char *>(msg.msg.data()),
- msg.msg.size());
- }
-
- return android::hardware::Void();
- }
-
- virtual Return<void> handleHubEvent(AsyncEventType evt) {
- if (evt == AsyncEventType::RESTARTED) {
- ALOGW("Context Hub handle %d restarted", mContextHubId);
- onHubReset(mContextHubId);
- } else {
- ALOGW("Cannot handle event %u from hub %d", evt, mContextHubId);
- }
-
- return android::hardware::Void();
- }
-
- virtual Return<void> handleTxnResult(uint32_t txnId,
- TransactionResult result) {
- ALOGI("Handle transaction result , hubId %" PRIu32 ", txnId %" PRIu32 ", result %" PRIu32,
- mContextHubId,
- txnId,
- result);
-
- switch(txnId) {
- case CONTEXT_HUB_APPS_ENABLE:
- case CONTEXT_HUB_APPS_DISABLE:
- passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
- break;
-
- case CONTEXT_HUB_UNLOAD_APP:
- closeUnloadTxn(result == TransactionResult::SUCCESS);
- passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
- break;
-
- case CONTEXT_HUB_LOAD_APP:
- {
- jint appInstanceHandle = INVALID_APP_ID;
- bool appRunningOnHub = (result == TransactionResult::SUCCESS);
- if (!(closeLoadTxn(appRunningOnHub, &appInstanceHandle))) {
- if (appRunningOnHub) {
- // Now we're in an odd situation. Our nanoapp
- // is up and running on the Context Hub. However,
- // something went wrong in our Service code so that
- // we're not able to properly track this nanoapp
- // in our Service code. If we tell the Java layer
- // things are good, it's a lie because the handle
- // we give them will fail when used with the Service.
- // If we tell the Java layer this failed, it's kind
- // of a lie as well, since this nanoapp is running.
- //
- // We leave a more robust fix for later, and for
- // now just tell the user things have failed.
- //
- // TODO(b/30835981): Make this situation better.
- result = TransactionResult::FAILURE;
- }
- }
-
- passOnOsResponse(mContextHubId,
- txnId,
- result,
- reinterpret_cast<int8_t *>(&appInstanceHandle),
- sizeof(appInstanceHandle));
- break;
- }
-
- default:
- ALOGI("unrecognized transction id %" PRIu32, txnId);
- break;
- }
- return android::hardware::Void();
- }
-
- virtual Return<void> handleAppsInfo(
- const android::hardware::hidl_vec<HubAppInfo>& apps) {
- TransactionResult result = TransactionResult::SUCCESS;
- handleQueryAppsResponse(apps,mContextHubId);
- passOnOsResponse(mContextHubId, CONTEXT_HUB_QUERY_APPS, result, nullptr, 0);
- return android::hardware::Void();
- }
-
- virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) {
- ALOGI("Handle app aport called from %" PRIx64 " with abort code %" PRIu32,
- appId,
- abortCode);
-
- // TODO: Plumb this to the clients interested in this app
- return android::hardware::Void();
- }
-
- void setContextHubId(uint32_t id) {
- mContextHubId = id;
- }
-
- uint32_t getContextHubId() {
- return(mContextHubId);
- }
-};
-
-struct AppInstanceInfo {
- HubAppInfo appInfo; // returned from the HAL
- uint64_t truncName; // Possibly truncated name for logging
- uint32_t hubHandle; // Id of the hub this app is on
- jint instanceId; // system wide unique instance id - assigned
-};
-
-struct ContextHubInfo {
- int numHubs;
- Vector<ContextHub> hubs;
- sp<IContexthub> contextHub;
-};
-
-struct ContextHubServiceDb {
- int initialized;
- ContextHubInfo hubInfo;
- JniInfo jniInfo;
- std::queue<jint> freeIds;
- std::unordered_map<jint, AppInstanceInfo> appInstances;
- TxnManager txnManager;
- std::vector<ContextHubServiceCallback *> regCallBacks;
-};
-
-ContextHubServiceDb db;
-
-bool getHubIdForHubHandle(int hubHandle, uint32_t *hubId) {
- if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs || hubId == nullptr) {
- return false;
- } else {
- *hubId = db.hubInfo.hubs[hubHandle].hubId;
- return true;
- }
-}
-
-int getHubHandleForAppInstance(jint id) {
- if (!db.appInstances.count(id)) {
- ALOGD("%s: Cannot find app for app instance %" PRId32,
- __FUNCTION__,
- id);
- return -1;
- }
-
- return db.appInstances[id].hubHandle;
-}
-
-jint getAppInstanceForAppId(uint64_t app_id) {
- auto end = db.appInstances.end();
- for (auto current = db.appInstances.begin(); current != end; ++current) {
- if (current->second.appInfo.appId == app_id) {
- return current->first;
- }
- }
- ALOGD("Cannot find app for app id %" PRIu64 ".", app_id);
- return -1;
-}
-
-uint64_t getAppIdForAppInstance(jint id) {
- if (!db.appInstances.count(id)) {
- return INVALID_APP_ID;
- }
- return db.appInstances[id].appInfo.appId;
-}
-
-void queryHubForApps(uint32_t hubId) {
- Result r = db.hubInfo.contextHub->queryApps(hubId);
- ALOGD("Sent query for apps to hub %" PRIu32 " with result %" PRIu32, hubId, r);
-}
-
-void sendQueryForApps() {
- for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
- queryHubForApps(db.hubInfo.hubs[i].hubId);
- }
-}
-
-int returnId(jint id) {
- // Note : This method is not thread safe.
- // id returned is guaranteed to be in use
- if (id >= 0) {
- db.freeIds.push(id);
- return 0;
- }
-
- return -1;
-}
-
-jint generateId() {
- // Note : This method is not thread safe.
- jint retVal = -1;
-
- if (!db.freeIds.empty()) {
- retVal = db.freeIds.front();
- db.freeIds.pop();
- }
-
- return retVal;
-}
-
-jint addAppInstance(const HubAppInfo *appInfo, uint32_t hubHandle,
- jint appInstanceHandle, JNIEnv *env) {
- // Not checking if the apps are indeed distinct
- AppInstanceInfo entry;
- assert(appInfo);
-
-
- entry.appInfo = *appInfo;
-
- entry.instanceId = appInstanceHandle;
- entry.truncName = appInfo->appId;
- entry.hubHandle = hubHandle;
- db.appInstances[appInstanceHandle] = entry;
- // Finally - let the service know of this app instance, to populate
- // the Java cache.
- env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceAddAppInstance,
- hubHandle, entry.instanceId,
- entry.truncName,
- entry.appInfo.version);
-
- const char *action = (db.appInstances.count(appInstanceHandle) == 0) ? "Added" : "Updated";
- ALOGI("%s App 0x%" PRIx64 " on hub Handle %" PRId32
- " as appInstance %" PRId32, action, entry.truncName,
- entry.hubHandle, appInstanceHandle);
-
- return appInstanceHandle;
-}
-
-int deleteAppInstance(jint id, JNIEnv *env) {
- bool fullyDeleted = true;
-
- if (db.appInstances.count(id)) {
- db.appInstances.erase(id);
- } else {
- ALOGW("Cannot delete App id (%" PRId32 ") from the JNI C++ cache", id);
- fullyDeleted = false;
- }
- returnId(id);
-
- if ((env == nullptr) ||
- (env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceDeleteAppInstance,
- id) != 0)) {
- ALOGW("Cannot delete App id (%" PRId32 ") from Java cache", id);
- fullyDeleted = false;
- }
-
- if (fullyDeleted) {
- ALOGI("Deleted App id : %" PRId32, id);
- return 0;
- }
- return -1;
-}
-
-int startLoadAppTxn(uint64_t appId, int hubHandle) {
- AppInstanceInfo *txnInfo = new AppInstanceInfo();
- jint instanceId = generateId();
-
- if (!txnInfo || instanceId < 0) {
- returnId(instanceId);
- delete txnInfo;
- return -1;
- }
-
- txnInfo->truncName = appId;
- txnInfo->hubHandle = hubHandle;
- txnInfo->instanceId = instanceId;
-
- txnInfo->appInfo.appId = appId;
- txnInfo->appInfo.version = -1; // Awaited
-
- if (db.txnManager.addTxn(CONTEXT_HUB_LOAD_APP, txnInfo) != 0) {
- returnId(instanceId);
- delete txnInfo;
- return -1;
- }
-
- return 0;
-}
-
-int startUnloadAppTxn(jint appInstanceHandle) {
- jint *txnData = new(jint);
- if (!txnData) {
- ALOGW("Cannot allocate memory to start unload transaction");
- return -1;
- }
-
- *txnData = appInstanceHandle;
-
- if (db.txnManager.addTxn(CONTEXT_HUB_UNLOAD_APP, txnData) != 0) {
- delete txnData;
- ALOGW("Cannot start transaction to unload app");
- return -1;
- }
-
- return 0;
-}
-
-void getHubsCb(const ::android::hardware::hidl_vec<ContextHub>& hubs) {
- for (size_t i = 0; i < hubs.size(); i++) {
- db.hubInfo.hubs.push_back(hubs[i]);
- }
-}
-
-void initContextHubService() {
- db.hubInfo.numHubs = 0;
-
- db.hubInfo.contextHub = IContexthub::getService();
-
- if (db.hubInfo.contextHub == nullptr) {
- ALOGE("Could not load context hub hal");
- } else {
- ALOGI("Loaded context hub hal, isRemote %s", db.hubInfo.contextHub->isRemote() ? "TRUE" : "FALSE");
- }
-
- // Prep for storing app info
- for (jint i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
- db.freeIds.push(i);
- }
-
- if (db.hubInfo.contextHub != nullptr) {
- std::function<void(const ::android::hardware::hidl_vec<ContextHub>& hubs)> f = getHubsCb;
- if(!db.hubInfo.contextHub->getHubs(f).isOk()) {
- ALOGW("GetHubs Failed! transport error.");
- return;
- };
-
- int retNumHubs = db.hubInfo.hubs.size();
- ALOGD("ContextHubModule returned %d hubs ", retNumHubs);
- db.hubInfo.numHubs = retNumHubs;
-
- for (int i = 0; i < db.hubInfo.numHubs; i++) {
- ALOGI("Subscribing to hubHandle %d", i);
-
- ContextHubServiceCallback *callBackPtr =
- new ContextHubServiceCallback(db.hubInfo.hubs[i].hubId);
- db.hubInfo.contextHub->registerCallback(db.hubInfo.hubs[i].hubId,
- callBackPtr);
- db.regCallBacks.push_back(callBackPtr);
- }
-
- sendQueryForApps();
-
- } else {
- ALOGW("No Context Hub Module present");
- }
-}
-
-void onHubReset(uint32_t hubId) {
- TransactionResult result = TransactionResult::SUCCESS;
- db.txnManager.closeTxn();
- // TODO : Expose this through an api
- passOnOsResponse(hubId, CONTEXT_HUB_OS_REBOOT, result, nullptr, 0);
- queryHubForApps(hubId);
-}
-
-int onMessageReceipt(const uint32_t *header,
- size_t headerLen,
- const char *msg,
- size_t msgLen) {
- JNIEnv *env;
-
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- return -1;
- }
-
- jbyteArray jmsg = env->NewByteArray(msgLen);
- if (jmsg == nullptr) {
- ALOGW("Can't allocate %zu byte array", msgLen);
- return -1;
- }
- jintArray jheader = env->NewIntArray(headerLen);
- if (jheader == nullptr) {
- env->DeleteLocalRef(jmsg);
- ALOGW("Can't allocate %zu int array", headerLen);
- return -1;
- }
-
- env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<const jbyte *>(msg));
- env->SetIntArrayRegion(jheader, 0, headerLen, reinterpret_cast<const jint *>(header));
-
- int ret = (env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceMsgReceiptCallback,
- jheader,
- jmsg) != 0);
- env->DeleteLocalRef(jmsg);
- env->DeleteLocalRef(jheader);
-
- return ret;
-}
-
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
- uint32_t hubHandle) {
- JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- return -1;
- }
-
- int numApps = apps.size();
-
- // We use this information to sync our JNI and Java caches of nanoapp info.
- // We want to accomplish two things here:
- // 1) Remove entries from our caches which are stale, and pertained to
- // apps no longer running on Context Hub.
- // 2) Populate our caches with the latest information of all these apps.
-
- // We make a couple of assumptions here:
- // A) The JNI and Java caches are in sync with each other (this isn't
- // necessarily true; any failure of a single call into Java land to
- // update its cache will leave that cache in a bad state. For NYC,
- // we're willing to tolerate this for now).
- // B) The total number of apps is relatively small, so horribly inefficent
- // algorithms aren't too painful.
- // C) We're going to call this relatively infrequently, so its inefficency
- // isn't a big impact.
-
-
- // (1). Looking for stale cache entries. Yes, this is O(N^2). See
- // assumption (B). Per assumption (A), it is sufficient to iterate
- // over just the JNI cache.
- auto end = db.appInstances.end();
- for (auto current = db.appInstances.begin(); current != end; ) {
- AppInstanceInfo cacheEntry = current->second;
- // We perform our iteration here because if we call
- // delete_app_instance() below, it will erase() this entry.
- current++;
- bool entryIsStale = true;
- for (int i = 0; i < numApps; i++) {
- if (apps[i].appId == cacheEntry.appInfo.appId) {
- // We found a match; this entry is current.
- entryIsStale = false;
- break;
- }
- }
-
- if (entryIsStale) {
- deleteAppInstance(cacheEntry.instanceId, env);
- }
- }
-
- // (2). Update our caches with the latest.
- for (int i = 0; i < numApps; i++) {
- // We will only have one instance of the app
- // TODO : Change this logic once we support multiple instances of the same app
- jint appInstance = getAppInstanceForAppId(apps[i].appId);
- if (appInstance == -1) {
- // This is a previously unknown app, let's allocate an "id" for it.
- appInstance = generateId();
- }
- addAppInstance(&apps[i], hubHandle, appInstance, env);
- }
- return 0;
-}
-
-// TODO(b/30807327): Do not use raw bytes for additional data. Use the
-// JNI interfaces for the appropriate types.
-void passOnOsResponse(uint32_t hubHandle,
- uint32_t msgType,
- TransactionResult result,
- const int8_t *additionalData,
- size_t additionalDataLen) {
- JNIEnv *env;
-
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- ALOGW("Cannot latch to JNI env, dropping OS response %" PRIu32,
- msgType);
- return;
- }
-
- uint32_t header[MSG_HEADER_SIZE];
- memset(header, 0, sizeof(header));
-
- if (!additionalData) {
- additionalDataLen = 0; // clamp
- }
- int msgLen = 1 + additionalDataLen;
-
- int8_t *msg = new int8_t[msgLen];
-
- if (!msg) {
- ALOGW("Unexpected : Ran out of memory, cannot send response");
- return;
- }
-
- header[HEADER_FIELD_MSG_TYPE] = msgType;
- header[HEADER_FIELD_MSG_VERSION] = 0;
- header[HEADER_FIELD_HUB_HANDLE] = hubHandle;
- header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID;
-
- // Due to API constraints, at the moment we can't change the fact that
- // we're changing our 4-byte response to a 1-byte value. But we can prevent
- // the possible change in sign (and thus meaning) that would happen from
- // a naive cast. Further, we can log when we're losing part of the value.
- // TODO(b/30918279): Don't truncate this result.
- int8_t truncatedResult;
- truncatedResult = static_cast<int8_t>(result);
- msg[0] = truncatedResult;
-
- if (additionalData) {
- memcpy(&msg[1], additionalData, additionalDataLen);
- }
-
- jbyteArray jmsg = env->NewByteArray(msgLen);
- jintArray jheader = env->NewIntArray(arraysize(header));
-
- env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<jbyte *>(msg));
- env->SetIntArrayRegion(jheader, 0, arraysize(header), reinterpret_cast<jint *>(header));
-
- ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
- header[HEADER_FIELD_MSG_TYPE],
- header[HEADER_FIELD_APP_INSTANCE],
- header[HEADER_FIELD_HUB_HANDLE]);
-
- env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceMsgReceiptCallback,
- jheader,
- jmsg);
-
- env->DeleteLocalRef(jmsg);
- env->DeleteLocalRef(jheader);
-
- delete[] msg;
-}
-
-void closeUnloadTxn(bool success) {
- void *txnData = nullptr;
- HubMessageType txnId;
-
- if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
- txnId == CONTEXT_HUB_UNLOAD_APP) {
- JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- ALOGW("Could not attach to JVM !");
- env = nullptr;
- }
- jint handle = *reinterpret_cast<jint *>(txnData);
- deleteAppInstance(handle, env);
- } else {
- ALOGW("Could not unload the app successfully ! success %d, txnData %p",
- success,
- txnData);
- }
-
- db.txnManager.closeTxn();
-}
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle) {
- void *txnData;
- HubMessageType txnId;
-
- if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
- txnId == CONTEXT_HUB_LOAD_APP) {
- AppInstanceInfo *info = static_cast<AppInstanceInfo *>(txnData);
- *appInstanceHandle = info->instanceId;
-
- JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) == JNI_OK) {
- addAppInstance(&info->appInfo, info->hubHandle, info->instanceId, env);
- } else {
- ALOGW("Could not attach to JVM !");
- success = false;
- }
- // While we just called addAppInstance above, our info->appInfo was
- // incomplete (for example, the 'version' is hardcoded to -1). So we
- // trigger an additional query to the CHRE, so we'll be able to get
- // all the app "info", and have our JNI and Java caches with the
- // full information.
- sendQueryForApps();
- } else {
- ALOGW("Could not load the app successfully ! Unexpected failure");
- *appInstanceHandle = INVALID_APP_ID;
- success = false;
- }
-
- db.txnManager.closeTxn();
- return success;
-}
-
-int initJni(JNIEnv *env, jobject instance) {
- if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) {
- return -1;
- }
-
- db.jniInfo.jContextHubService = env->NewGlobalRef(instance);
-
- db.jniInfo.contextHubInfoClass =
- env->FindClass("android/hardware/location/ContextHubInfo");
- db.jniInfo.contextHubServiceClass =
- env->FindClass("com/android/server/location/ContextHubService");
-
- db.jniInfo.memoryRegionsClass =
- env->FindClass("android/hardware/location/MemoryRegion");
-
- db.jniInfo.contextHubInfoCtor =
- env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V");
- db.jniInfo.contextHubInfoSetId =
- env->GetMethodID(db.jniInfo.contextHubInfoClass, "setId", "(I)V");
- db.jniInfo.contextHubInfoSetName =
- env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", "(Ljava/lang/String;)V");
- db.jniInfo.contextHubInfoSetVendor =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setVendor",
- "(Ljava/lang/String;)V");
- db.jniInfo.contextHubInfoSetToolchain =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setToolchain",
- "(Ljava/lang/String;)V");
- db.jniInfo.contextHubInfoSetPlatformVersion =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setPlatformVersion",
- "(I)V");
- db.jniInfo.contextHubInfoSetStaticSwVersion =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setStaticSwVersion",
- "(I)V");
- db.jniInfo.contextHubInfoSetToolchainVersion =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setToolchainVersion",
- "(I)V");
- db.jniInfo.contextHubInfoSetPeakMips =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setPeakMips",
- "(F)V");
- db.jniInfo.contextHubInfoSetStoppedPowerDrawMw =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setStoppedPowerDrawMw",
- "(F)V");
- db.jniInfo.contextHubInfoSetSleepPowerDrawMw =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setSleepPowerDrawMw",
- "(F)V");
- db.jniInfo.contextHubInfoSetPeakPowerDrawMw =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setPeakPowerDrawMw",
- "(F)V");
- db.jniInfo.contextHubInfoSetSupportedSensors =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setSupportedSensors",
- "([I)V");
- db.jniInfo.contextHubInfoSetMemoryRegions =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setMemoryRegions",
- "([Landroid/hardware/location/MemoryRegion;)V");
- db.jniInfo.contextHubInfoSetMaxPacketLenBytes =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setMaxPacketLenBytes",
- "(I)V");
- db.jniInfo.contextHubServiceMsgReceiptCallback =
- env->GetMethodID(db.jniInfo.contextHubServiceClass,
- "onMessageReceipt",
- "([I[B)I");
- db.jniInfo.contextHubInfoSetName =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setName",
- "(Ljava/lang/String;)V");
- db.jniInfo.contextHubServiceAddAppInstance =
- env->GetMethodID(db.jniInfo.contextHubServiceClass,
- "addAppInstance",
- "(IIJI)I");
- db.jniInfo.contextHubServiceDeleteAppInstance =
- env->GetMethodID(db.jniInfo.contextHubServiceClass,
- "deleteAppInstance",
- "(I)I");
-
- return 0;
-}
-
-jobject constructJContextHubInfo(JNIEnv *env, const ContextHub &hub) {
- jstring jstrBuf;
- jintArray jintBuf;
- jobjectArray jmemBuf;
-
- jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass,
- db.jniInfo.contextHubInfoCtor);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub.hubId);
-
- jstrBuf = env->NewStringUTF(hub.name.c_str());
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf);
- env->DeleteLocalRef(jstrBuf);
-
- jstrBuf = env->NewStringUTF(hub.vendor.c_str());
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf);
- env->DeleteLocalRef(jstrBuf);
-
- jstrBuf = env->NewStringUTF(hub.toolchain.c_str());
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf);
- env->DeleteLocalRef(jstrBuf);
-
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub.platformVersion);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub.toolchainVersion);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub.peakMips);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw,
- hub.stoppedPowerDrawMw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw,
- hub.sleepPowerDrawMw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw,
- hub.peakPowerDrawMw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes,
- hub.maxSupportedMsgLen);
-
-
- jintBuf = env->NewIntArray(hub.connectedSensors.size());
- int *connectedSensors = new int[hub.connectedSensors.size()];
-
- if (!connectedSensors) {
- ALOGW("Cannot allocate memory! Unexpected");
- assert(false);
- } else {
- for (unsigned int i = 0; i < hub.connectedSensors.size(); i++) {
- // TODO :: Populate connected sensors.
- //connectedSensors[i] = hub.connectedSensors[i].sensorType;
- connectedSensors[i] = 0;
- }
- }
-
- env->SetIntArrayRegion(jintBuf, 0, hub.connectedSensors.size(),
- connectedSensors);
-
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf);
- env->DeleteLocalRef(jintBuf);
-
- // We are not getting the memory regions from the CH Hal - change this when it is available
- jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr);
- // Note the zero size above. We do not need to set any elements
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf);
- env->DeleteLocalRef(jmemBuf);
-
-
- delete[] connectedSensors;
- return jHub;
-}
-
-jobjectArray nativeInitialize(JNIEnv *env, jobject instance) {
- jobject hub;
- jobjectArray retArray;
-
- if (initJni(env, instance) < 0) {
- return nullptr;
- }
-
- initContextHubService();
-
- if (db.hubInfo.numHubs > 1) {
- ALOGW("Clamping the number of hubs to 1");
- db.hubInfo.numHubs = 1;
- }
-
- retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr);
-
- for(int i = 0; i < db.hubInfo.numHubs; i++) {
- hub = constructJContextHubInfo(env, db.hubInfo.hubs[i]);
- env->SetObjectArrayElement(retArray, i, hub);
- }
-
- return retArray;
-}
-
-Result sendLoadNanoAppRequest(uint32_t hubId,
- jbyte *data,
- size_t dataBufferLength) {
- auto header = reinterpret_cast<const NanoAppBinaryHeader *>(data);
- Result result;
-
- if (dataBufferLength < sizeof(NanoAppBinaryHeader)) {
- ALOGE("Got short NanoApp, length %zu", dataBufferLength);
- result = Result::BAD_PARAMS;
- } else if (header->headerVersion != htole32(kNanoAppBinaryHeaderVersion)) {
- ALOGE("Got unexpected NanoApp header version %" PRIu32,
- letoh32(header->headerVersion));
- result = Result::BAD_PARAMS;
- } else {
- NanoAppBinary nanoapp;
-
- // Data from the common nanoapp header goes into explicit fields
- nanoapp.appId = letoh64(header->appId);
- nanoapp.appVersion = letoh32(header->appVersion);
- nanoapp.flags = letoh32(header->flags);
- nanoapp.targetChreApiMajorVersion = header->targetChreApiMajorVersion;
- nanoapp.targetChreApiMinorVersion = header->targetChreApiMinorVersion;
-
- // Everything past the header goes in customBinary
- auto dataBytes = reinterpret_cast<const uint8_t *>(data);
- std::vector<uint8_t> customBinary(
- dataBytes + sizeof(NanoAppBinaryHeader),
- dataBytes + dataBufferLength);
- nanoapp.customBinary = std::move(customBinary);
-
- ALOGW("Calling Load NanoApp on hub %d", hubId);
- result = db.hubInfo.contextHub->loadNanoApp(hubId,
- nanoapp,
- CONTEXT_HUB_LOAD_APP);
- }
-
- return result;
-}
-
-jint nativeSendMessage(JNIEnv *env,
- jobject instance,
- jintArray header_,
- jbyteArray data_) {
- // With the new binderized HAL definition, this function can be made much simpler.
- // All the magic can be removed. This is not however needed for the default implementation
- // TODO :: Change the JNI interface to conform to the new HAL interface and clean up this
- // function
- jint retVal = -1; // Default to failure
-
- jint *header = env->GetIntArrayElements(header_, 0);
- size_t numHeaderElements = env->GetArrayLength(header_);
- jbyte *data = env->GetByteArrayElements(data_, 0);
- size_t dataBufferLength = env->GetArrayLength(data_);
-
- if (numHeaderElements < MSG_HEADER_SIZE) {
- ALOGW("Malformed header len");
- return -1;
- }
-
- jint appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE];
- uint32_t msgType = header[HEADER_FIELD_MSG_TYPE];
- int hubHandle = -1;
- uint64_t appId;
-
- if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- hubHandle = getHubHandleForAppInstance(appInstanceHandle);
- } else if (msgType == CONTEXT_HUB_LOAD_APP) {
- if (numHeaderElements < MSG_HEADER_SIZE_LOAD_APP) {
- return -1;
- }
- uint64_t appIdLo = header[HEADER_FIELD_LOAD_APP_ID_LO];
- uint64_t appIdHi = header[HEADER_FIELD_LOAD_APP_ID_HI];
- appId = appIdHi << 32 | appIdLo;
-
- hubHandle = header[HEADER_FIELD_HUB_HANDLE];
- } else {
- hubHandle = header[HEADER_FIELD_HUB_HANDLE];
- }
-
- uint32_t hubId = -1;
- if (!getHubIdForHubHandle(hubHandle, &hubId)) {
- ALOGD("Invalid hub Handle %d", hubHandle);
- return -1;
- }
-
- if (msgType == CONTEXT_HUB_LOAD_APP ||
- msgType == CONTEXT_HUB_UNLOAD_APP) {
-
- db.txnManager.closeAnyStaleTxns();
-
- if (db.txnManager.isTxnPending()) {
- // TODO : There is a race conditio
- ALOGW("Cannot load or unload app while a transaction is pending !");
- return -1;
- } else if (msgType == CONTEXT_HUB_LOAD_APP) {
- if (startLoadAppTxn(appId, hubHandle) != 0) {
- ALOGW("Cannot Start Load Transaction");
- return -1;
- }
- } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- if (startUnloadAppTxn(appInstanceHandle) != 0) {
- ALOGW("Cannot Start UnLoad Transaction");
- return -1;
- }
- }
- }
-
- Result result;
-
- if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- ALOGW("Calling UnLoad NanoApp for app %" PRIx64 " on hub %" PRIu32,
- db.appInstances[appInstanceHandle].appInfo.appId,
- hubId);
- result = db.hubInfo.contextHub->unloadNanoApp(
- hubId, db.appInstances[appInstanceHandle].appInfo.appId, CONTEXT_HUB_UNLOAD_APP);
- } else {
- if (appInstanceHandle == OS_APP_ID) {
- if (msgType == CONTEXT_HUB_LOAD_APP) {
- result = sendLoadNanoAppRequest(hubId, data, dataBufferLength);
- } else if (msgType == CONTEXT_HUB_QUERY_APPS) {
- result = db.hubInfo.contextHub->queryApps(hubId);
- } else {
- ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType);
- result = Result::BAD_PARAMS;
- }
- } else {
- appId = getAppIdForAppInstance(appInstanceHandle);
- if (appId == static_cast<uint64_t>(INVALID_APP_ID)) {
- ALOGD("Cannot find application instance %d", appInstanceHandle);
- result = Result::BAD_PARAMS;
- } else if (hubHandle != getHubHandleForAppInstance(appInstanceHandle)) {
- ALOGE("Given hubHandle (%d) doesn't match expected for app instance (%d)",
- hubHandle,
- getHubHandleForAppInstance(appInstanceHandle));
- result = Result::BAD_PARAMS;
- } else {
- ContextHubMsg msg;
- msg.appName = appId;
- msg.msgType = msgType;
- msg.msg.setToExternal((unsigned char *)data, dataBufferLength);
-
- ALOGW("Sending msg of type %" PRIu32 " len %zu to app %" PRIx64 " on hub %" PRIu32,
- msgType,
- dataBufferLength,
- appId,
- hubId);
- result = db.hubInfo.contextHub->sendMessageToHub(hubId, msg);
- }
- }
- }
-
- if (result != Result::OK) {
- ALOGD("Send Message failure - %d", result);
- if (msgType == CONTEXT_HUB_LOAD_APP) {
- jint ignored;
- closeLoadTxn(false, &ignored);
- } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- closeUnloadTxn(false);
- }
- } else {
- retVal = 0;
- }
-
- env->ReleaseIntArrayElements(header_, header, 0);
- env->ReleaseByteArrayElements(data_, data, 0);
-
- return retVal;
-}
-
-//--------------------------------------------------------------------------------------------------
-//
-const JNINativeMethod gContextHubServiceMethods[] = {
- {"nativeInitialize",
- "()[Landroid/hardware/location/ContextHubInfo;",
- reinterpret_cast<void*>(nativeInitialize)},
- {"nativeSendMessage",
- "([I[B)I",
- reinterpret_cast<void*>(nativeSendMessage)}
-};
-
-int register_android_server_location_ContextHubService(JNIEnv *env)
-{
- RegisterMethodsOrDie(env, "com/android/server/location/ContextHubService",
- gContextHubServiceMethods, NELEM(gContextHubServiceMethods));
-
- return 0;
-}
-
-}//namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 2f45181e6f5c..46d50435282d 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,7 +39,6 @@ int register_android_server_UsbMidiDevice(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_vr_VrManagerService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_location_ContextHubService(JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
@@ -82,7 +81,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_vr_VrManagerService(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
- register_android_server_location_ContextHubService(env);
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 74a7bd4a8c0b..33f4e3487f76 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -35,6 +35,7 @@ import android.os.FileUtils;
import android.os.IIncidentManager;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.PowerManager;
import android.os.Process;
import android.os.ServiceManager;
@@ -360,6 +361,9 @@ public final class SystemServer {
// to avoid throwing BadParcelableException.
BaseBundle.setShouldDefuse(true);
+ // Within the system server, when parceling exceptions, include the stack trace
+ Parcel.setStackTraceParceling(true);
+
// Ensure binder calls into the system always run at foreground priority.
BinderInternal.disableBackgroundScheduling(true);
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index f63d1e725792..114929ccecfd 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -48,7 +48,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
# TODO(b/69254249): Migrate to Robolectric 3.4.2
LOCAL_JAVA_LIBRARIES := \
junit \
- platform-robolectric-prebuilt
+ platform-robolectric-3.1.1-prebuilt
LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
LOCAL_MODULE := FrameworksServicesRoboTests
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 70e8a162b1cf..c145e828c49c 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -144,7 +144,9 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
- public TestableNotificationManagerService(Context context) { super(context); }
+ public TestableNotificationManagerService(Context context) {
+ super(context);
+ }
@Override
protected boolean isCallingUidSystem() {
@@ -160,6 +162,11 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
protected ICompanionDeviceManager getCompanionManager() {
return null;
}
+
+ @Override
+ protected void reportSeen(NotificationRecord r) {
+ return;
+ }
}
@Before
@@ -1865,8 +1872,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
}
@Test
- public void testStats_updatedOnExpansion() throws Exception {
- final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ public void testStats_updatedOnUserExpansion() throws Exception {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
@@ -1876,6 +1883,17 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
}
@Test
+ public void testStats_notUpdatedOnAutoExpansion() throws Exception {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+ assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
+ assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ }
+
+ @Test
public void testStats_updatedOnViewSettings() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f1e76abf2200..9a5ebedcf361 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index f22dfdcadc2c..c7fd5510ebe1 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -11,7 +11,6 @@ import android.content.pm.ChangedPackages;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstrumentationInfo;
@@ -623,12 +622,6 @@ public class PackageManagerStub extends PackageManager {
}
@Override
- public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName) {
-
- }
-
- @Override
public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags,
String installerPackageName) {
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index afe432b96f85..467b47acd48a 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,7 +37,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
@@ -57,10 +56,10 @@ import org.junit.runner.RunWith;
* To run this test from root of checkout:
* <pre>
* mmm -j32 frameworks/base/services/tests/servicestests/
- * adb install out/target/product/marlin/data/app/JobTestApp/JobTestApp.apk
- * adb install out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * adb install -r $OUT/data/app/JobTestApp/JobTestApp.apk
+ * adb install -r $OUT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
* adb shell am instrument -e class 'com.android.server.job.BackgroundRestrictionsTest' -w \
- * 'com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner'
+ com.android.frameworks.servicestests
* </pre>
*/
@RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 4db9a30a11ca..36d0c8bec09e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -17,12 +17,15 @@
package com.android.server.pm.dex;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.pm.Installer;
+
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
@@ -36,8 +39,13 @@ import java.util.List;
import java.util.Map;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,6 +53,12 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
@@ -56,6 +70,12 @@ public class DexManagerTests {
private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
DelegateLastClassLoader.class.getName();
+ @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+ @Mock Installer mInstaller;
+ @Mock IPackageManager mPM;
+ private final Object mInstallLock = new Object();
+ @Mock DexManager.Listener mListener;
+
private DexManager mDexManager;
private TestData mFooUser0;
@@ -90,7 +110,8 @@ public class DexManagerTests {
mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
DELEGATE_LAST_CLASS_LOADER_NAME);
- mDexManager = new DexManager(null, null, null, null);
+ mDexManager = new DexManager(
+ mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener);
// Foo and Bar are available to user0.
// Only Bar is available to user1;
@@ -440,6 +461,20 @@ public class DexManagerTests {
}
+ @Test
+ public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
+ notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+ when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
+ .thenReturn(mFooUser0.mPackageInfo);
+
+ mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
+
+ verify(mListener, times(fooSecondaries.size()))
+ .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
+ any(DexUseInfo.class), anyString(), anyInt());
+ }
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
@@ -492,12 +527,12 @@ public class DexManagerTests {
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
- assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
- return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+ assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
+ return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
}
private void assertNoUseInfo(TestData testData) {
- assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+ assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
}
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -555,8 +590,8 @@ public class DexManagerTests {
List<String> getSecondaryDexPathsFromProtectedDirs() {
List<String> paths = new ArrayList<>();
- paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
- paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
+ paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
+ paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
return paths;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 5b6225e79f8e..0db19e452650 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -18,13 +18,13 @@ package com.android.server.power;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
import android.os.Handler;
-import android.provider.Settings;
import android.provider.Settings.Global;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import com.android.frameworks.servicestests.R;
+import com.android.internal.annotations.VisibleForTesting;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -68,6 +68,12 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
int getDeviceSpecificConfigResId() {
return mDeviceSpecificConfigResId;
}
+
+
+ @VisibleForTesting
+ void onChange() {
+ onChange(true, null);
+ }
}
@Mock
@@ -221,14 +227,14 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
mMockGlobalSettings.put(Global.BATTERY_SAVER_CONSTANTS, "");
mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, "");
- mBatterySaverPolicy.onChangeForTest();
+ mBatterySaverPolicy.onChange();
assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_2;
- mBatterySaverPolicy.onChangeForTest();
+ mBatterySaverPolicy.onChange();
assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
assertThat(mBatterySaverPolicy.getFileValues(false).toString())
.isEqualTo("{/sys/a=1, /sys/b=2}");
@@ -236,7 +242,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_3;
- mBatterySaverPolicy.onChangeForTest();
+ mBatterySaverPolicy.onChange();
assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{/proc/c=4}");
assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{/sys/a=3}");
@@ -244,7 +250,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
"file-on:/proc/z=4");
- mBatterySaverPolicy.onChangeForTest();
+ mBatterySaverPolicy.onChange();
assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{/proc/z=4}");
assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 8531baf5f329..40edfd214dfe 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -17,6 +17,9 @@
package com.android.server.usage;
import static android.app.usage.AppStandby.*;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -67,6 +70,10 @@ public class AppStandbyControllerTests {
private static final long HOUR_MS = 60 * MINUTE_MS;
private static final long DAY_MS = 24 * HOUR_MS;
+ private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
+ private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
+ private static final long RARE_THRESHOLD = 48 * HOUR_MS;
+
private MyInjector mInjector;
static class MyContextWrapper extends ContextWrapper {
@@ -168,6 +175,14 @@ public class AppStandbyControllerTests {
return packageName != null && packageName.equals(mBoundWidgetPackage);
}
+ @Override
+ String getAppIdleSettings() {
+ return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
+ + WORKING_SET_THRESHOLD + "/"
+ + FREQUENT_THRESHOLD + "/"
+ + RARE_THRESHOLD;
+ }
+
// Internal methods
void setDisplayOn(boolean on) {
@@ -225,12 +240,12 @@ public class AppStandbyControllerTests {
AppStandbyController controller = setupController();
setChargingState(controller, true);
- mInjector.mElapsedRealtime = 8 * DAY_MS;
+ mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
setChargingState(controller, false);
- mInjector.mElapsedRealtime = 16 * DAY_MS;
+ mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
controller.checkIdleStates(USER_ID);
assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
@@ -247,43 +262,49 @@ public class AppStandbyControllerTests {
false));
}
- private void reportEvent(AppStandbyController controller, long elapsedTime) {
+ private void reportEvent(AppStandbyController controller, int eventType,
+ long elapsedTime) {
// Back to ACTIVE on event
UsageEvents.Event ev = new UsageEvents.Event();
ev.mPackage = PACKAGE_1;
- ev.mEventType = UsageEvents.Event.USER_INTERACTION;
+ ev.mEventType = eventType;
controller.reportEvent(ev, elapsedTime, USER_ID);
}
+ private int getStandbyBucket(AppStandbyController controller) {
+ return controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+ true);
+ }
+
@Test
public void testBuckets() throws Exception {
AppStandbyController controller = setupController();
assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
- reportEvent(controller, 0);
+ reportEvent(controller, USER_INTERACTION, 0);
// ACTIVE bucket
- assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
// WORKING_SET bucket
- assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
// WORKING_SET bucket
- assertTimeout(controller, 47 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(controller, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
// FREQUENT bucket
- assertTimeout(controller, 4 * DAY_MS, STANDBY_BUCKET_FREQUENT);
+ assertTimeout(controller, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
// RARE bucket
- assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
+ assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
- reportEvent(controller, 9 * DAY_MS);
+ reportEvent(controller, USER_INTERACTION, RARE_THRESHOLD + 1);
- assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
// RARE bucket
- assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+ assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
}
@Test
@@ -293,23 +314,21 @@ public class AppStandbyControllerTests {
assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
- reportEvent(controller, 0);
+ reportEvent(controller, USER_INTERACTION, 0);
// ACTIVE bucket
- assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
// WORKING_SET bucket
- assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
// RARE bucket, should fail because the screen wasn't ON.
- mInjector.mElapsedRealtime = 9 * DAY_MS;
+ mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
controller.checkIdleStates(USER_ID);
- assertNotEquals(STANDBY_BUCKET_RARE,
- controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
- false));
+ assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
mInjector.setDisplayOn(true);
- assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+ assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
}
@Test
@@ -318,8 +337,7 @@ public class AppStandbyControllerTests {
setChargingState(controller, false);
controller.forceIdleState(PACKAGE_1, USER_ID, true);
- assertEquals(STANDBY_BUCKET_RARE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
- true));
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
controller.forceIdleState(PACKAGE_1, USER_ID, false);
@@ -327,4 +345,20 @@ public class AppStandbyControllerTests {
true));
assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
}
+
+ @Test
+ public void testNotificationEvent() throws Exception {
+ AppStandbyController controller = setupController();
+ setChargingState(controller, false);
+
+ reportEvent(controller, USER_INTERACTION, 0);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+ mInjector.mElapsedRealtime = 1;
+ reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+
+ controller.forceIdleState(PACKAGE_1, USER_ID, true);
+ reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 05c4853612a3..2224de56f21c 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -31,6 +31,7 @@ import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Base64;
+import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -1628,10 +1629,10 @@ public class WebViewUpdateServiceTest {
newSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
PackageInfo currentSdkPackage = createPackageInfo("currentTargetSdkPackage",
true /* enabled */, true /* valid */, true /* installed */);
- currentSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1+1;
+ currentSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK;
PackageInfo oldSdkPackage = createPackageInfo("oldTargetSdkPackage",
true /* enabled */, true /* valid */, true /* installed */);
- oldSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ oldSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK - 1;
WebViewProviderInfo newSdkProviderInfo =
new WebViewProviderInfo(newSdkPackage.packageName, "", true, false, null);
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 606088128fc4..b55c79b4957d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -129,6 +129,9 @@ public class AppWindowContainerControllerTests extends WindowTestsBase {
controller.removeStartingWindow();
waitUntilHandlersIdle();
assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
+
+ controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+ mDisplayContent.onPendingTransactionApplied();
}
}
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 bb882641c55f..d9ab5c86bc72 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,71 +51,77 @@ import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
public class AppWindowTokenTests extends WindowTestsBase {
+ TaskStack mStack;
+ Task mTask;
+ WindowTestUtils.TestAppWindowToken mToken;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mStack = createTaskStackOnDisplay(mDisplayContent);
+ mTask = createTaskInStack(mStack, 0 /* userId */);
+ mToken = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+
+ mTask.addChild(mToken, 0);
+ }
+
@Test
@Presubmit
public void testAddWindow_Order() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-
- assertEquals(0, token.getWindowsCount());
+ assertEquals(0, mToken.getWindowsCount());
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1");
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token,
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
"startingWin");
- final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin");
- final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4");
+ final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
+ final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4");
// Should not contain the windows that were added above.
- assertEquals(4, token.getWindowsCount());
- assertTrue(token.hasWindow(win1));
- assertTrue(token.hasWindow(startingWin));
- assertTrue(token.hasWindow(baseWin));
- assertTrue(token.hasWindow(win4));
+ assertEquals(4, mToken.getWindowsCount());
+ assertTrue(mToken.hasWindow(win1));
+ assertTrue(mToken.hasWindow(startingWin));
+ assertTrue(mToken.hasWindow(baseWin));
+ assertTrue(mToken.hasWindow(win4));
// The starting window should be on-top of all other windows.
- assertEquals(startingWin, token.getLastChild());
+ assertEquals(startingWin, mToken.getLastChild());
// The base application window should be below all other windows.
- assertEquals(baseWin, token.getFirstChild());
- token.removeImmediately();
+ assertEquals(baseWin, mToken.getFirstChild());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testFindMainWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertNull(mToken.findMainWindow());
- assertNull(token.findMainWindow());
-
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- assertEquals(window1, token.findMainWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
+ final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
+ assertEquals(window1, mToken.findMainWindow());
window1.mAnimatingExit = true;
- assertEquals(window1, token.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
- assertEquals(window2, token.findMainWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.findMainWindow());
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+ assertEquals(window2, mToken.findMainWindow());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testGetTopFullscreenWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
-
- assertNull(token.getTopFullscreenWindow());
+ assertNull(mToken.getTopFullscreenWindow());
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(null, TYPE_APPLICATION, token, "window11");
- final WindowState window12 = createWindow(null, TYPE_APPLICATION, token, "window12");
- assertEquals(window12, token.getTopFullscreenWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
+ final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
+ assertEquals(window12, mToken.getTopFullscreenWindow());
window12.mAttrs.width = 500;
- assertEquals(window11, token.getTopFullscreenWindow());
+ assertEquals(window11, mToken.getTopFullscreenWindow());
window11.mAttrs.width = 500;
- assertEquals(window1, token.getTopFullscreenWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.getTopFullscreenWindow());
+ mToken.removeImmediately();
}
@Test
@@ -124,27 +130,21 @@ public class AppWindowTokenTests extends WindowTestsBase {
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
appWindow.resizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
@@ -159,18 +159,11 @@ public class AppWindowTokenTests extends WindowTestsBase {
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked();
- final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(defaultDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
performRotation(Surface.ROTATION_90);
@@ -193,53 +186,49 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
@Presubmit
public void testGetOrientation() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- token.setFillsParent(false);
+ mToken.setFillsParent(false);
// Can specify orientation if app doesn't fill parent.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
- token.setFillsParent(true);
- token.hidden = true;
- token.sendingToBottom = true;
+ mToken.setFillsParent(true);
+ mToken.hidden = true;
+ mToken.sendingToBottom = true;
// Can not specify orientation if app isn't visible even though it fills parent.
- assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
- token.sendingToBottom = false;
- token.setIsOnTop(true);
- // Allow for token to provide orientation hidden if on top and not being sent to bottom.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ mToken.sendingToBottom = false;
+ mToken.setIsOnTop(true);
+ // Allow for mToken to provide orientation hidden if on top and not being sent to bottom.
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
}
@Test
@Presubmit
public void testKeyguardFlagsDuringRelaunch() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, token);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
// Add window with show when locked flag
- token.addWindow(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.addWindow(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Start relaunching
- token.startRelaunching();
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.startRelaunching();
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Remove window and make sure that we still report back flag
- token.removeChild(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.removeChild(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Finish relaunching and ensure flag is now not reported
- token.finishRelaunching();
- assertFalse(token.containsShowWhenLockedWindow() || token.containsDismissKeyguardWindow());
+ mToken.finishRelaunching();
+ assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
deleted file mode 100644
index c3a471a828b0..000000000000
--- a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.hardware.display.DisplayManagerGlobal;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link DimLayerController} class.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
- */
-@SmallTest
-@Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
-public class DimLayerControllerTests extends WindowTestsBase {
-
- /**
- * This tests if shared fullscreen dim layer is added when stack is added to display
- * and is removed when the only stack on the display is removed.
- */
- @Test
- public void testSharedFullScreenDimLayer() throws Exception {
- // Create a display.
- final DisplayContent dc = createNewDisplay();
- assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
- // Add stack with activity.
- final TaskStack stack = createTaskStackOnDisplay(dc);
- assertTrue(dc.mDimLayerController.hasDimLayerUser(stack));
- assertTrue(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
- // Remove the only stack on the display and check if the shared dim layer clears.
- stack.removeImmediately();
- assertFalse(dc.mDimLayerController.hasDimLayerUser(stack));
- assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
new file mode 100644
index 000000000000..f069d49a244b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -0,0 +1,207 @@
+/*
+ * 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 java.util.HashMap;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DimmerTests extends WindowTestsBase {
+ private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+ final SurfaceControl mControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+
+ @Override
+ SurfaceControl getSurfaceControl() {
+ return mControl;
+ }
+
+ @Override
+ SurfaceControl.Transaction getPendingTransaction() {
+ return mTransaction;
+ }
+ }
+
+ private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+ final SurfaceSession mSession = new SurfaceSession();
+ SurfaceControl mBuiltSurface = null;
+ final SurfaceControl mHostControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
+
+ class MockSurfaceBuilder extends SurfaceControl.Builder {
+ MockSurfaceBuilder(SurfaceSession ss) {
+ super(ss);
+ }
+
+ @Override
+ public SurfaceControl build() {
+ SurfaceControl sc = mock(SurfaceControl.class);
+ mBuiltSurface = sc;
+ return sc;
+ }
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ return new MockSurfaceBuilder(mSession);
+ }
+
+ @Override
+ SurfaceControl getSurfaceControl() {
+ return mHostControl;
+ }
+
+ @Override
+ SurfaceControl.Transaction getPendingTransaction() {
+ return mHostTransaction;
+ }
+ }
+
+ MockSurfaceBuildingContainer mHost;
+ Dimmer mDimmer;
+ SurfaceControl.Transaction mTransaction;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mHost = new MockSurfaceBuildingContainer();
+ mTransaction = mock(SurfaceControl.Transaction.class);
+ mDimmer = new Dimmer(mHost);
+ }
+
+ @Test
+ public void testDimAboveNoChildCreatesSurface() throws Exception {
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE);
+ }
+
+ @Test
+ public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
+ float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, alpha);
+ final SurfaceControl firstSurface = mHost.mBuiltSurface;
+
+ alpha = 0.9f;
+ mDimmer.dimAbove(mTransaction, alpha);
+
+ assertEquals(firstSurface, mHost.mBuiltSurface);
+ verify(mTransaction).setAlpha(firstSurface, 0.9f);
+ }
+
+ @Test
+ public void testUpdateDimsAppliesSize() throws Exception {
+ mDimmer.dimAbove(mTransaction, 0.8f);
+
+ int width = 100;
+ int height = 300;
+ Rect bounds = new Rect(0, 0, width, height);
+ mDimmer.updateDims(mTransaction, bounds);
+ verify(mTransaction).setSize(mHost.mBuiltSurface, width, height);
+ }
+
+ @Test
+ public void testDimAboveNoChildNotReset() throws Exception {
+ mDimmer.dimAbove(mTransaction, 0.8f);
+ mDimmer.resetDimStates();
+
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface, never()).destroy();
+ }
+
+ @Test
+ public void testDimAboveWithChildCreatesSurfaceAboveChild() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimBelow(mTransaction, child, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceDestroyedWhenReset() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface).destroy();
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ mDimmer.resetDimStates();
+ mDimmer.dimAbove(mTransaction, child, alpha);
+
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface, never()).destroy();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 887def71ba66..873a01bd8116 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -66,7 +66,7 @@ public class TaskPositionerTests extends WindowTestsBase {
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
mPositioner = new TaskPositioner(sWm);
- mPositioner.register(display);
+ mPositioner.register(mDisplayContent);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
index b846fd02840b..0ef78f4527c3 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
@@ -105,14 +105,10 @@ public class TaskStackTests extends WindowTestsBase {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
assertEquals(stack, task.mStack);
- assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
- assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
// Remove stack and check if its child is also removed.
stack.removeImmediately();
assertNull(stack.getDisplayContent());
assertNull(task.mStack);
- assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
- assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 53d0bfbb5cf7..a45695ffb5be 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -568,11 +568,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public boolean canMagnifyWindow(int windowType) {
- return false;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
deleted file mode 100644
index 3c3514f36315..000000000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-/**
- * Tests for the {@link WindowLayersController} class.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowLayersControllerTests extends WindowTestsBase {
-
- @Test
- public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
- sWm.mInputMethodTarget = null;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The Ime has an higher base layer than app windows and lower base layer than system
- // windows, so it should be above app windows and below system windows if there isn't an IME
- // target.
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows and below system windows if it is targeting an app
- // window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
- "imeAppTargetChildAboveWindow");
- final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
- "imeAppTargetChildBelowWindow");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for child windows that are z-ordered above it
- // and below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(imeAppTargetChildAboveWindow, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, imeAppTargetChildBelowWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
- final WindowState appBelowImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState appAboveImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for non-fullscreen app window above it and
- // below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, appBelowImeTarget);
- assertWindowLayerGreaterThan(appAboveImeTarget, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
- final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
- mDisplayContent, "imeSystemOverlayTarget",
- true /* ownerCanAddInternalSystemWindow */);
-
- sWm.mInputMethodTarget = imeSystemOverlayTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The IME target base layer is higher than all window except for the nav bar window, so the
- // IME should be above all windows except for the nav bar.
- assertWindowLayerGreaterThan(mImeWindow, imeSystemOverlayTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mImeWindow, mStatusBarWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testStackLayers() throws Exception {
- WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
- WindowState dockedStackWindow = createWindowOnStack(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
- mDisplayContent, "dockedStackWindow");
- WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
- mDisplayContent, "assistantStackWindow");
-
- mLayersController.assignWindowLayers(mDisplayContent);
-
- assertWindowLayerGreaterThan(dockedStackWindow, mAppWindow);
- assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow);
- assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow);
- }
-
- private void assertWindowLayerGreaterThan(WindowState first, WindowState second)
- throws Exception {
- assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer);
- }
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 0980f7ec4df0..4c5e291784d6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -73,7 +73,6 @@ class WindowTestsBase {
private static boolean sOneTimeSetupDone = false;
DisplayContent mDisplayContent;
DisplayInfo mDisplayInfo = new DisplayInfo();
- WindowLayersController mLayersController;
WindowState mWallpaperWindow;
WindowState mImeWindow;
WindowState mImeDialogWindow;
@@ -98,8 +97,9 @@ class WindowTestsBase {
final Context context = InstrumentationRegistry.getTargetContext();
AttributeCache.init(context);
+
sWm = TestWindowManagerPolicy.getWindowManagerService(context);
- mLayersController = new WindowLayersController(sWm);
+ beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
@@ -126,6 +126,10 @@ class WindowTestsBase {
waitUntilHandlersIdle();
}
+ void beforeCreateDisplay() {
+ // Called before display is created.
+ }
+
@After
public void tearDown() throws Exception {
final LinkedList<WindowState> nonCommonWindows = new LinkedList();
@@ -149,6 +153,14 @@ class WindowTestsBase {
waitUntilHandlersIdle();
}
+ /**
+ * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
+ * set-up (or null).
+ */
+ SurfaceBuilderFactory getSurfaceBuilderFactory() {
+ return null;
+ }
+
private WindowState createCommonWindow(WindowState parent, int type, String name) {
final WindowState win = createWindow(parent, type, name);
mCommonWindows.add(win);
@@ -162,6 +174,11 @@ class WindowTestsBase {
Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
}
+ /** Asserts that the first entry is greater than the second entry. */
+ void assertLessThan(int first, int second) throws Exception {
+ Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
+ }
+
/**
* Waits until the main handler for WM has processed all messages.
*/
@@ -264,7 +281,7 @@ class WindowTestsBase {
final int displayId = sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- return new DisplayContent(display, sWm, mLayersController, new WallpaperController(sWm));
+ return new DisplayContent(display, sWm, new WallpaperController(sWm));
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 692e08baf7db..7219104b6931 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -94,43 +94,6 @@ public class WindowTokenTests extends WindowTestsBase {
assertEquals(null, dc.getWindowToken(token.token));
}
- @Test
- public void testAdjustAnimLayer() throws Exception {
- final WindowTestUtils.TestWindowToken token =
- new WindowTestUtils.TestWindowToken(0, mDisplayContent);
- final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
- final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
-
- window2.mLayer = 100;
- window3.mLayer = 200;
-
- // We assign layers once, to get the base values computed by
- // the controller.
- mLayersController.assignWindowLayers(mDisplayContent);
-
- final int window1StartLayer = window1.mWinAnimator.mAnimLayer;
- final int window11StartLayer = window11.mWinAnimator.mAnimLayer;
- final int window12StartLayer = window12.mWinAnimator.mAnimLayer;
- final int window2StartLayer = window2.mWinAnimator.mAnimLayer;
- final int window3StartLayer = window3.mWinAnimator.mAnimLayer;
-
- // Then we set an adjustment, and assign them again, they should
- // be offset.
- int adj = token.adj = 50;
- mLayersController.assignWindowLayers(mDisplayContent);
- final int highestLayer = token.getHighestAnimLayer();
-
- assertEquals(window1StartLayer + adj, window1.mWinAnimator.mAnimLayer);
- assertEquals(window11StartLayer + adj, window11.mWinAnimator.mAnimLayer);
- assertEquals(window12StartLayer + adj, window12.mWinAnimator.mAnimLayer);
- assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, highestLayer);
- }
-
/**
* Test that a window token isn't orphaned by the system when it is requested to be removed.
* Tokens should only be removed from the system when all their windows are gone.
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
new file mode 100644
index 000000000000..f7c4b1f51c46
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Log;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+/**
+ * Tests for the {@link WindowLayersController} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ZOrderingTests extends WindowTestsBase {
+
+ private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+ HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
+ HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
+
+ @Override
+ public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
+ mRelativeLayersForControl.remove(sc);
+ mLayersForControl.put(sc, layer);
+ return super.setLayer(sc, layer);
+ }
+
+ @Override
+ public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
+ SurfaceControl relativeTo,
+ int layer) {
+ mRelativeLayersForControl.put(sc, relativeTo);
+ mLayersForControl.put(sc, layer);
+ return super.setRelativeLayer(sc, relativeTo, layer);
+ }
+
+ int getLayer(SurfaceControl sc) {
+ return mLayersForControl.get(sc);
+ }
+
+ SurfaceControl getRelativeLayer(SurfaceControl sc) {
+ return mRelativeLayersForControl.get(sc);
+ }
+ };
+
+ // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+ // such that we can keep track of the parents of Surfaces as they are constructed.
+ private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
+
+ private class HierarchyRecorder extends SurfaceControl.Builder {
+ SurfaceControl mPendingParent;
+
+ HierarchyRecorder(SurfaceSession s) {
+ super(s);
+ }
+
+ public SurfaceControl.Builder setParent(SurfaceControl sc) {
+ mPendingParent = sc;
+ return super.setParent(sc);
+ }
+ public SurfaceControl build() {
+ SurfaceControl sc = super.build();
+ mParentFor.put(sc, mPendingParent);
+ mPendingParent = null;
+ return sc;
+ }
+ };
+
+ class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+ public SurfaceControl.Builder make(SurfaceSession s) {
+ return new HierarchyRecorder(s);
+ }
+ };
+
+ private LayerRecordingTransaction mTransaction;
+
+ @Override
+ void beforeCreateDisplay() {
+ // We can't use @Before here because it may happen after WindowTestsBase @Before
+ // which is after construction of the DisplayContent, meaning the HierarchyRecorder
+ // would miss construction of the top-level layers.
+ mTransaction = new LayerRecordingTransaction();
+ sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
+ }
+
+ @After
+ public void after() {
+ mTransaction.close();
+ mParentFor.clear();
+ }
+
+ LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+ LinkedList<SurfaceControl> p = new LinkedList();
+ SurfaceControl current = sc;
+ do {
+ p.addLast(current);
+
+ SurfaceControl rs = t.getRelativeLayer(current);
+ if (rs != null) {
+ current = rs;
+ } else {
+ current = mParentFor.get(current);
+ }
+ } while (current != null);
+ return p;
+ }
+
+ void assertZOrderGreaterThan(LayerRecordingTransaction t,
+ SurfaceControl left, SurfaceControl right) throws Exception {
+ final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
+ final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
+
+ SurfaceControl commonAncestor = null;
+ SurfaceControl leftTop = leftParentChain.peekLast();
+ SurfaceControl rightTop = rightParentChain.peekLast();
+ while (leftTop != null && rightTop != null && leftTop == rightTop) {
+ commonAncestor = leftParentChain.removeLast();
+ rightParentChain.removeLast();
+ leftTop = leftParentChain.peekLast();
+ rightTop = rightParentChain.peekLast();
+ }
+
+ if (rightTop == null) { // right is the parent of left.
+ assertGreaterThan(t.getLayer(leftTop), 0);
+ } else if (leftTop == null) { // left is the parent of right.
+ assertGreaterThan(0, t.getLayer(rightTop));
+ } else {
+ assertGreaterThan(t.getLayer(leftTop),
+ t.getLayer(rightTop));
+ }
+ }
+
+ void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
+ WindowState left, WindowState right) throws Exception {
+ assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
+ sWm.mInputMethodTarget = null;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The Ime has an higher base layer than app windows and lower base layer than system
+ // windows, so it should be above app windows and below system windows if there isn't an IME
+ // target.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows and below system windows if it is targeting an app
+ // window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
+ "imeAppTargetChildAboveWindow");
+ final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
+ "imeAppTargetChildBelowWindow");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for child windows that are z-ordered above it
+ // and below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
+ final WindowState appBelowImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState appAboveImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for non-fullscreen app window above it and
+ // below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
+ assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
+ final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
+ mDisplayContent, "imeSystemOverlayTarget",
+ true /* ownerCanAddInternalSystemWindow */);
+
+ sWm.mInputMethodTarget = imeSystemOverlayTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The IME target base layer is higher than all window except for the nav bar window, so the
+ // IME should be above all windows except for the nav bar.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+
+ // The IME has a higher base layer than the status bar so we may expect it to go
+ // above the status bar once they are both in the Non-App layer, as past versions of this
+ // test enforced. However this seems like the wrong behavior unless the status bar is the
+ // IME target.
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
+ sWm.mInputMethodTarget = mStatusBarWindow;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testStackLayers() throws Exception {
+ final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+ "pinnedStackWindow");
+ final WindowState dockedStackWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "dockedStackWindow");
+ final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+ mDisplayContent, "assistantStackWindow");
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
+ assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index c5ca33041f41..7ca17afdcbeb 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -91,8 +91,6 @@ public class AppIdleHistory {
private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
private long mScreenOnDuration; // Total screen on duration since device was "born"
- private long mElapsedTimeThreshold;
- private long mScreenOnTimeThreshold;
private final File mStorageDir;
private boolean mScreenOn;
@@ -113,11 +111,6 @@ public class AppIdleHistory {
readScreenOnTime();
}
- public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
- mElapsedTimeThreshold = elapsedTimeThreshold;
- mScreenOnTimeThreshold = screenOnTimeThreshold;
- }
-
public void updateDisplay(boolean screenOn, long elapsedRealtime) {
if (screenOn == mScreenOn) return;
@@ -186,7 +179,7 @@ public class AppIdleHistory {
writeScreenOnTime();
}
- public void reportUsage(String packageName, int userId, long elapsedRealtime) {
+ public int reportUsage(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime, true);
@@ -197,12 +190,33 @@ public class AppIdleHistory {
+ (elapsedRealtime - mElapsedSnapshot);
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
- appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_ACTIVE;
+ if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+ if (DEBUG) {
+ Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ + ", reason=" + appUsageHistory.bucketingReason);
+ }
+ }
appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
- if (DEBUG) {
- Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+
+ return appUsageHistory.currentBucket;
+ }
+
+ public int reportMildUsage(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtime, true);
+ if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
+ if (DEBUG) {
+ Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ + ", reason=" + appUsageHistory.bucketingReason);
+ }
}
+ // TODO: Should this be a different reason for partial usage?
+ appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+
+ return appUsageHistory.currentBucket;
}
public void setIdle(String packageName, int userId, long elapsedRealtime) {
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 5623a68f158d..cd0fce6c852c 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -91,14 +91,14 @@ public class AppStandbyController {
0,
0,
COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
- COMPRESS_TIME ? 240 * 1000 : 8 * ONE_HOUR
+ COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
};
static final long[] ELAPSED_TIME_THRESHOLDS = {
0,
COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
- COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_DAY,
- COMPRESS_TIME ? 16 * ONE_MINUTE : 8 * ONE_DAY
+ COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
+ COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
};
static final int[] THRESHOLD_BUCKETS = {
@@ -140,9 +140,7 @@ public class AppStandbyController {
static final int MSG_PAROLE_STATE_CHANGED = 9;
static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
- long mAppIdleScreenThresholdMillis;
long mCheckIdleIntervalMillis;
- long mAppIdleWallclockThresholdMillis;
long mAppIdleParoleIntervalMillis;
long mAppIdleParoleDurationMillis;
long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
@@ -229,6 +227,7 @@ public class AppStandbyController {
// Get sync adapters for the authority
String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
authority, userId);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
for (String packageName: packages) {
// Only force the sync adapters to active if the provider is not in the same package and
// the sync adapter is a system package.
@@ -239,7 +238,12 @@ public class AppStandbyController {
continue;
}
if (!packageName.equals(providerPkgName)) {
- setAppIdleAsync(packageName, false, userId);
+ synchronized (mAppIdleLock) {
+ int newBucket = mAppIdleHistory.reportMildUsage(packageName, userId,
+ elapsedRealtime);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ newBucket);
+ }
}
} catch (PackageManager.NameNotFoundException e) {
// Shouldn't happen
@@ -493,11 +497,21 @@ public class AppStandbyController {
if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
|| event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
- || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
- mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+ || event.mEventType == UsageEvents.Event.USER_INTERACTION
+ || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) {
+
+ final int newBucket;
+ if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+ newBucket = mAppIdleHistory.reportMildUsage(event.mPackage, userId,
+ elapsedRealtime);
+ } else {
+ newBucket = mAppIdleHistory.reportUsage(event.mPackage, userId,
+ elapsedRealtime);
+ }
+
+ maybeInformListeners(event.mPackage, userId, elapsedRealtime,
+ newBucket);
if (previouslyIdle) {
- maybeInformListeners(event.mPackage, userId, elapsedRealtime,
- AppStandby.STANDBY_BUCKET_ACTIVE);
notifyBatteryStats(event.mPackage, userId, false);
}
}
@@ -896,14 +910,6 @@ public class AppStandbyController {
pw.println();
pw.println("Settings:");
- pw.print(" mAppIdleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleWallclockThresholdMillis=");
- TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
- pw.println();
-
pw.print(" mCheckIdleIntervalMillis=");
TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
pw.println();
@@ -1033,6 +1039,11 @@ public class AppStandbyController {
int userId) {
return appWidgetManager.isBoundWidgetPackage(packageName, userId);
}
+
+ String getAppIdleSettings() {
+ return Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.APP_IDLE_CONSTANTS);
+ }
}
class AppStandbyHandler extends Handler {
@@ -1165,31 +1176,18 @@ public class AppStandbyController {
// Look at global settings for this.
// TODO: Maybe apply different thresholds for different users.
try {
- mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS));
+ mParser.setString(mInjector.getAppIdleSettings());
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
// fallthrough, mParser is empty and all defaults will be returned.
}
- // Default: 12 hours of screen-on time sans dream-time
- mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
- mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
- COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
- mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
- COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
-
// Default: 24 hours between paroles
mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
- mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
- mAppIdleScreenThresholdMillis);
String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
@@ -1199,6 +1197,9 @@ public class AppStandbyController {
null);
mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
ELAPSED_TIME_THRESHOLDS);
+ mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
+ COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
+
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 0b1059045b5b..f02221cb1699 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -643,6 +643,8 @@ class UserUsageStatsService {
return "SHORTCUT_INVOCATION";
case UsageEvents.Event.CHOOSER_ACTION:
return "CHOOSER_ACTION";
+ case UsageEvents.Event.NOTIFICATION_SEEN:
+ return "NOTIFICATION_SEEN";
default:
return "UNKNOWN";
}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index f30d7bdef810..05480dc38a0d 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -1065,7 +1065,7 @@ public final class RemoteConnection {
*
* @param state The audio state of this {@code RemoteConnection}.
* @hide
- * @deprecated Use {@link #setCallAudioState(CallAudioState) instead.
+ * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
*/
@SystemApi
@Deprecated
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 6276626afae2..c9684062b6c3 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -101,9 +101,6 @@ public final class CellIdentityGsm implements Parcelable {
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
*
- * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
- * not a 2 or 3-digit code.
- *
* @hide
*/
public CellIdentityGsm (int lac, int cid, int arfcn, int bsic, String mccStr,
@@ -115,22 +112,29 @@ public final class CellIdentityGsm implements Parcelable {
// for inbound parcels
mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
+ // Only allow INT_MAX if unknown string mcc/mnc
if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
mMccStr = mccStr;
- } else if (mccStr.isEmpty()) {
- // If the mccStr parsed from Parcel is empty, set it as null.
+ } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mccStr is empty or unknown, set it as null.
mMccStr = null;
} else {
- throw new IllegalArgumentException("invalid MCC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+ // after the bug got fixed.
+ mMccStr = null;
+ log("invalid MCC format: " + mccStr);
}
if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
mMncStr = mncStr;
- } else if (mncStr.isEmpty()) {
- // If the mncStr parsed from Parcel is empty, set it as null.
+ } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mncStr is empty or unknown, set it as null.
mMncStr = null;
} else {
- throw new IllegalArgumentException("invalid MNC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+ // after the bug got fixed.
+ mMncStr = null;
+ log("invalid MNC format: " + mncStr);
}
mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 74d2966b25bc..825dcc336eb9 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -102,9 +102,6 @@ public final class CellIdentityLte implements Parcelable {
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
*
- * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
- * not a 2 or 3-digit code.
- *
* @hide
*/
public CellIdentityLte (int ci, int pci, int tac, int earfcn, String mccStr,
@@ -114,22 +111,29 @@ public final class CellIdentityLte implements Parcelable {
mTac = tac;
mEarfcn = earfcn;
+ // Only allow INT_MAX if unknown string mcc/mnc
if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
mMccStr = mccStr;
- } else if (mccStr.isEmpty()) {
- // If the mccStr parsed from Parcel is empty, set it as null.
+ } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mccStr is empty or unknown, set it as null.
mMccStr = null;
} else {
- throw new IllegalArgumentException("invalid MCC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+ // after the bug got fixed.
+ mMccStr = null;
+ log("invalid MCC format: " + mccStr);
}
if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
mMncStr = mncStr;
- } else if (mncStr.isEmpty()) {
- // If the mncStr parsed from Parcel is empty, set it as null.
+ } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mncStr is empty or unknown, set it as null.
mMncStr = null;
} else {
- throw new IllegalArgumentException("invalid MNC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+ // after the bug got fixed.
+ mMncStr = null;
+ log("invalid MNC format: " + mncStr);
}
mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 51b11aa82b9a..e74b5701233f 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -102,9 +102,6 @@ public final class CellIdentityWcdma implements Parcelable {
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
*
- * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
- * not a 2 or 3-digit code.
- *
* @hide
*/
public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
@@ -114,22 +111,29 @@ public final class CellIdentityWcdma implements Parcelable {
mPsc = psc;
mUarfcn = uarfcn;
+ // Only allow INT_MAX if unknown string mcc/mnc
if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
mMccStr = mccStr;
- } else if (mccStr.isEmpty()) {
- // If the mccStr parsed from Parcel is empty, set it as null.
+ } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mccStr is empty or unknown, set it as null.
mMccStr = null;
} else {
- throw new IllegalArgumentException("invalid MCC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+ // after the bug got fixed.
+ mMccStr = null;
+ log("invalid MCC format: " + mccStr);
}
if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
mMncStr = mncStr;
- } else if (mncStr.isEmpty()) {
- // If the mncStr parsed from Parcel is empty, set it as null.
+ } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mncStr is empty or unknown, set it as null.
mMncStr = null;
} else {
- throw new IllegalArgumentException("invalid MNC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+ // after the bug got fixed.
+ mMncStr = null;
+ log("invalid MNC format: " + mncStr);
}
mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2f39ddb1a79e..1e6abf22f7ef 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -28,6 +28,7 @@ import android.content.res.Resources;
import android.net.INetworkPolicyManager;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -361,6 +362,9 @@ public class SubscriptionManager {
/**
* TelephonyProvider column name for enable Volte.
+ *
+ * If this setting is not initialized (set to -1) then we use the Carrier Config value
+ * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
*@hide
*/
public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
@@ -446,7 +450,15 @@ public class SubscriptionManager {
* for #onSubscriptionsChanged to be invoked.
*/
public static class OnSubscriptionsChangedListener {
- private final Handler mHandler = new Handler() {
+ private class OnSubscriptionsChangedListenerHandler extends Handler {
+ OnSubscriptionsChangedListenerHandler() {
+ super();
+ }
+
+ OnSubscriptionsChangedListenerHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
if (DBG) {
@@ -454,7 +466,22 @@ public class SubscriptionManager {
}
OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
}
- };
+ }
+
+ private final Handler mHandler;
+
+ public OnSubscriptionsChangedListener() {
+ mHandler = new OnSubscriptionsChangedListenerHandler();
+ }
+
+ /**
+ * Allow a listener to be created with a custom looper
+ * @param looper the looper that the underlining handler should run on
+ * @hide
+ */
+ public OnSubscriptionsChangedListener(Looper looper) {
+ mHandler = new OnSubscriptionsChangedListenerHandler(looper);
+ }
/**
* Callback invoked when there is any change to any SubscriptionInfo. Typically
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
new file mode 100644
index 000000000000..18da8b8bd687
--- /dev/null
+++ b/test-mock/Android.mk
@@ -0,0 +1,144 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock)
+
+# Build the repackaged.android.test.mock library
+# ==============================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
+
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt
+
+LOCAL_MODULE:= repackaged.android.test.mock
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Build the android.test.mock library
+# ===================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
+
+LOCAL_MODULE:= android.test.mock
+
+include $(BUILD_JAVA_LIBRARY)
+
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
+# Generate the stub source files for android.test.mock.stubs
+# ==========================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(android_test_mock_source_files)
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
+
+ANDROID_TEST_MOCK_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/api.txt
+ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/removed.txt
+
+ANDROID_TEST_MOCK_API_FILE := $(LOCAL_PATH)/api/android-test-mock-current.txt
+ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
+
+LOCAL_DROIDDOC_OPTIONS:= \
+ -stubpackages android.test.mock \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/src \
+ -nodocs \
+ -api $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) \
+ -removedApi $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) \
+
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_MODULE := android-test-mock-api-stubs-gen
+
+include $(BUILD_DROIDDOC)
+
+# Remember the target that will trigger the code generation.
+android_test_mock_gen_stamp := $(full_target)
+
+# Add some additional dependencies
+$(ANDROID_TEST_MOCK_OUTPUT_API_FILE): $(full_target)
+$(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE): $(full_target)
+
+# Build the android.test.mock.stubs library
+# =========================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.mock.stubs
+
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+
+# Make sure to run droiddoc first to generate the stub source files.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
+android_test_mock_gen_stamp :=
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.mock.stubs.jar)
+
+# Check that the android.test.mock.stubs library has not changed
+# ==============================================================
+
+# Check that the API we're building hasn't changed from the not-yet-released
+# SDK version.
+$(eval $(call check-api, \
+ check-android-test-mock-api-current, \
+ $(ANDROID_TEST_MOCK_API_FILE), \
+ $(ANDROID_TEST_MOCK_OUTPUT_API_FILE), \
+ $(ANDROID_TEST_MOCK_REMOVED_API_FILE), \
+ $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE), \
+ -error 2 -error 3 -error 4 -error 5 -error 6 \
+ -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+ -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
+ -error 25 -error 26 -error 27, \
+ cat $(LOCAL_PATH)/api/apicheck_msg_android_test_mock.txt, \
+ check-android-test-mock-api, \
+ $(call doc-timestamp-for,android-test-mock-api-stubs-gen) \
+ ))
+
+.PHONY: check-android-test-mock-api
+checkapi: check-android-test-mock-api
+
+.PHONY: update-android-test-mock-api
+update-api: update-android-test-mock-api
+
+update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP)
+ @echo Copying current.txt
+ $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) $(ANDROID_TEST_MOCK_API_FILE)
+ @echo Copying removed.txt
+ $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
+
+# Build the android.test.mock.sdk library
+# =======================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.mock.sdk
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := android.test.mock.stubs
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/test-runner/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
index 93bbf6c5c024..07fcee244deb 100644
--- a/test-runner/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -344,7 +344,6 @@ package android.test.mock {
method public int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public int installExistingPackageAsUser(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void installPackage(android.net.Uri, android.content.pm.IPackageInstallObserver, int, java.lang.String);
method public void installPackage(android.net.Uri, android.app.PackageInstallObserver, int, java.lang.String);
method public boolean isInstantApp();
method public boolean isInstantApp(java.lang.String);
diff --git a/test-runner/api/android-test-mock-removed.txt b/test-mock/api/android-test-mock-removed.txt
index 9920f63d1632..9920f63d1632 100644
--- a/test-runner/api/android-test-mock-removed.txt
+++ b/test-mock/api/android-test-mock-removed.txt
diff --git a/test-runner/api/apicheck_msg_android_test_mock.txt b/test-mock/api/apicheck_msg_android_test_mock.txt
index e388935bf798..e388935bf798 100644
--- a/test-runner/api/apicheck_msg_android_test_mock.txt
+++ b/test-mock/api/apicheck_msg_android_test_mock.txt
diff --git a/test-runner/src/android/test/mock/MockApplication.java b/test-mock/src/android/test/mock/MockApplication.java
index 3257ecf11066..3257ecf11066 100644
--- a/test-runner/src/android/test/mock/MockApplication.java
+++ b/test-mock/src/android/test/mock/MockApplication.java
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index d5f3ce880b8f..d5f3ce880b8f 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
diff --git a/test-runner/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java
index a70152c8b732..a70152c8b732 100644
--- a/test-runner/src/android/test/mock/MockContentResolver.java
+++ b/test-mock/src/android/test/mock/MockContentResolver.java
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 5e5ba462cfca..5e5ba462cfca 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
diff --git a/test-runner/src/android/test/mock/MockCursor.java b/test-mock/src/android/test/mock/MockCursor.java
index 576f24ad6384..576f24ad6384 100644
--- a/test-runner/src/android/test/mock/MockCursor.java
+++ b/test-mock/src/android/test/mock/MockCursor.java
diff --git a/test-runner/src/android/test/mock/MockDialogInterface.java b/test-mock/src/android/test/mock/MockDialogInterface.java
index d0a5a097918d..d0a5a097918d 100644
--- a/test-runner/src/android/test/mock/MockDialogInterface.java
+++ b/test-mock/src/android/test/mock/MockDialogInterface.java
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 112d7eef3dbe..112d7eef3dbe 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 7e08f51cd87d..0c562e65bc85 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -26,12 +26,11 @@ import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
-import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstantAppInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
@@ -653,15 +652,6 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
- /**
- * @hide - to match hiding in superclass
- */
- @Override
- public void installPackage(Uri packageURI, IPackageInstallObserver observer,
- int flags, String installerPackageName) {
- throw new UnsupportedOperationException();
- }
-
@Override
public void setInstallerPackageName(String targetPackage,
String installerPackageName) {
diff --git a/test-runner/src/android/test/mock/MockResources.java b/test-mock/src/android/test/mock/MockResources.java
index 880343e5e780..880343e5e780 100644
--- a/test-runner/src/android/test/mock/MockResources.java
+++ b/test-mock/src/android/test/mock/MockResources.java
diff --git a/test-runner/src/android/test/mock/package.html b/test-mock/src/android/test/mock/package.html
index c0fcd1ea336a..c0fcd1ea336a 100644
--- a/test-runner/src/android/test/mock/package.html
+++ b/test-mock/src/android/test/mock/package.html
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 3367aba5c0f9..d0f5b3248c29 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -16,14 +16,11 @@
LOCAL_PATH:= $(call my-dir)
-android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock)
-
# Build the android.test.runner library
# =====================================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- $(filter-out $(android_test_mock_source_files), $(call all-java-files-under, src))
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := \
core-oj \
@@ -42,7 +39,12 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
+LOCAL_JAVA_LIBRARIES := \
+ core-oj \
+ core-libart \
+ framework \
+ legacy-test \
+ android.test.mock \
LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt
@@ -57,10 +59,7 @@ ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
# ============================================================
include $(CLEAR_VARS)
-# Exclude android.test.mock classes as stubs for them are created in the
-# android.test.mock.stubs target
-LOCAL_SRC_FILES := \
- $(filter-out $(android_test_mock_source_files), $(call all-java-files-under, src))
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := \
core-oj \
@@ -153,117 +152,3 @@ update-android-test-runner-api: $(ANDROID_TEST_RUNNER_OUTPUT_API_FILE) | $(ACP)
$(hide) $(ACP) $(ANDROID_TEST_RUNNER_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_RUNNER_REMOVED_API_FILE)
endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
-
-# Build the android.test.mock library
-# ===================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(android_test_mock_source_files)
-
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-
-LOCAL_MODULE:= android.test.mock
-
-include $(BUILD_JAVA_LIBRARY)
-
-# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
-ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
-
-# Generate the stub source files for android.test.mock.stubs
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(android_test_mock_source_files)
-
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
-
-ANDROID_TEST_MOCK_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/api.txt
-ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/removed.txt
-
-ANDROID_TEST_MOCK_API_FILE := $(LOCAL_PATH)/api/android-test-mock-current.txt
-ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
-
-LOCAL_DROIDDOC_OPTIONS:= \
- -stubpackages android.test.mock \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/src \
- -nodocs \
- -api $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) \
- -removedApi $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) \
-
-LOCAL_UNINSTALLABLE_MODULE := true
-LOCAL_MODULE := android-test-mock-api-stubs-gen
-
-include $(BUILD_DROIDDOC)
-
-# Remember the target that will trigger the code generation.
-android_test_mock_gen_stamp := $(full_target)
-
-# Add some additional dependencies
-$(ANDROID_TEST_MOCK_OUTPUT_API_FILE): $(full_target)
-$(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE): $(full_target)
-
-# Build the android.test.mock.stubs library
-# =========================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android.test.mock.stubs
-
-LOCAL_SOURCE_FILES_ALL_GENERATED := true
-
-# Make sure to run droiddoc first to generate the stub source files.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
-android_test_mock_gen_stamp :=
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Archive a copy of the classes.jar in SDK build.
-$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.mock.stubs.jar)
-
-# Check that the android.test.mock.stubs library has not changed
-# ==============================================================
-
-# Check that the API we're building hasn't changed from the not-yet-released
-# SDK version.
-$(eval $(call check-api, \
- check-android-test-mock-api-current, \
- $(ANDROID_TEST_MOCK_API_FILE), \
- $(ANDROID_TEST_MOCK_OUTPUT_API_FILE), \
- $(ANDROID_TEST_MOCK_REMOVED_API_FILE), \
- $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE), \
- -error 2 -error 3 -error 4 -error 5 -error 6 \
- -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
- -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
- -error 25 -error 26 -error 27, \
- cat $(LOCAL_PATH)/api/apicheck_msg_android_test_mock.txt, \
- check-android-test-mock-api, \
- $(call doc-timestamp-for,android-test-mock-api-stubs-gen) \
- ))
-
-.PHONY: check-android-test-mock-api
-checkapi: check-android-test-mock-api
-
-.PHONY: update-android-test-mock-api
-update-api: update-android-test-mock-api
-
-update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP)
- @echo Copying current.txt
- $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) $(ANDROID_TEST_MOCK_API_FILE)
- @echo Copying removed.txt
- $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
-
-# Build the android.test.mock.sdk library
-# =======================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android.test.mock.sdk
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := android.test.mock.stubs
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# additionally, build unit tests in a separate .apk
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 08efc276fe86..0f6fb50aba48 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -218,6 +218,7 @@ class ValueBodyPrinter : public ConstValueVisitor {
if (attr.id) {
printer_->Print(attr.id.value().to_string());
}
+ printer_->Println();
}
}
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 96a02033fc1d..6fcf0f6d19cd 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -268,6 +268,11 @@ inline ::std::ostream& operator<<(::std::ostream& out, const ResourceId& res_id)
return out << res_id.to_string();
}
+// For generic code to call 'using std::to_string; to_string(T);'.
+inline std::string to_string(const ResourceId& id) {
+ return id.to_string();
+}
+
//
// ResourceType implementation.
//
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 66b5a7af3677..e94c0b42daf1 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -16,7 +16,6 @@
#include <sys/stat.h>
-#include <fstream>
#include <queue>
#include <unordered_map>
#include <vector>
@@ -212,6 +211,8 @@ class LinkContext : public IAaptContext {
// This delegate will attempt to masquerade any '@id/' references with ID 0xPPTTEEEE,
// where PP > 7f, as 0x7fPPEEEE. Any potential overlapping is verified and an error occurs if such
// an overlap exists.
+//
+// See b/37498913.
class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
public:
FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
@@ -652,24 +653,26 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
static bool WriteStableIdMapToPath(IDiagnostics* diag,
const std::unordered_map<ResourceName, ResourceId>& id_map,
const std::string& id_map_path) {
- std::ofstream fout(id_map_path, std::ofstream::binary);
- if (!fout) {
- diag->Error(DiagMessage(id_map_path) << strerror(errno));
+ io::FileOutputStream fout(id_map_path);
+ if (fout.HadError()) {
+ diag->Error(DiagMessage(id_map_path) << "failed to open: " << fout.GetError());
return false;
}
+ text::Printer printer(&fout);
for (const auto& entry : id_map) {
const ResourceName& name = entry.first;
const ResourceId& id = entry.second;
- fout << name << " = " << id << "\n";
+ printer.Print(name.to_string());
+ printer.Print(" = ");
+ printer.Println(id.to_string());
}
+ fout.Flush();
- if (!fout) {
- diag->Error(DiagMessage(id_map_path) << "failed writing to file: "
- << android::base::SystemErrorCodeToString(errno));
+ if (fout.HadError()) {
+ diag->Error(DiagMessage(id_map_path) << "failed writing to file: " << fout.GetError());
return false;
}
-
return true;
}
@@ -985,36 +988,35 @@ class LinkCommand {
file::AppendPath(&out_path, "R.java");
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
- std::unique_ptr<std::ofstream> fout_text;
+ std::unique_ptr<io::FileOutputStream> fout_text;
if (out_text_symbols_path) {
- fout_text =
- util::make_unique<std::ofstream>(out_text_symbols_path.value(), std::ofstream::binary);
- if (!*fout_text) {
- context_->GetDiagnostics()->Error(
- DiagMessage() << "failed writing to '" << out_text_symbols_path.value()
- << "': " << android::base::SystemErrorCodeToString(errno));
+ fout_text = util::make_unique<io::FileOutputStream>(out_text_symbols_path.value());
+ if (fout_text->HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed writing to '" << out_text_symbols_path.value()
+ << "': " << fout_text->GetError());
return false;
}
}
JavaClassGenerator generator(context_, table, java_options);
if (!generator.Generate(package_name_to_generate, out_package, &fout, fout_text.get())) {
- context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.getError());
+ context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.GetError());
return false;
}
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
@@ -1142,18 +1144,19 @@ class LinkCommand {
file::AppendPath(&out_path, "Manifest.java");
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+ << "': " << fout.GetError());
return false;
}
- if (!ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout)) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout);
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
@@ -1165,19 +1168,19 @@ class LinkCommand {
}
const std::string& out_path = out.value();
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed to open '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+ << "': " << fout.GetError());
return false;
}
- proguard::WriteKeepSet(&fout, keep_set);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ proguard::WriteKeepSet(keep_set, &fout);
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
diff --git a/tools/aapt2/io/FileStream.cpp b/tools/aapt2/io/FileStream.cpp
index 4ff6d787cf0f..27529bc08a16 100644
--- a/tools/aapt2/io/FileStream.cpp
+++ b/tools/aapt2/io/FileStream.cpp
@@ -25,6 +25,12 @@
#include "android-base/macros.h"
#include "android-base/utf8.h"
+#if defined(_WIN32)
+// This is only needed for O_CLOEXEC.
+#include <windows.h>
+#define O_CLOEXEC O_NOINHERIT
+#endif
+
using ::android::base::SystemErrorCodeToString;
using ::android::base::unique_fd;
@@ -32,18 +38,20 @@ namespace aapt {
namespace io {
FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity)
- : FileInputStream(::android::base::utf8::open(path.c_str(), O_RDONLY | O_BINARY),
- buffer_capacity) {
+ : buffer_capacity_(buffer_capacity) {
+ int mode = O_RDONLY | O_CLOEXEC | O_BINARY;
+ fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode)));
+ if (fd_ == -1) {
+ error_ = SystemErrorCodeToString(errno);
+ } else {
+ buffer_.reset(new uint8_t[buffer_capacity_]);
+ }
}
FileInputStream::FileInputStream(int fd, size_t buffer_capacity)
- : fd_(fd),
- buffer_capacity_(buffer_capacity),
- buffer_offset_(0u),
- buffer_size_(0u),
- total_byte_count_(0u) {
- if (fd_ == -1) {
- error_ = SystemErrorCodeToString(errno);
+ : fd_(fd), buffer_capacity_(buffer_capacity) {
+ if (fd_ < 0) {
+ error_ = "Bad File Descriptor";
} else {
buffer_.reset(new uint8_t[buffer_capacity_]);
}
@@ -100,9 +108,16 @@ std::string FileInputStream::GetError() const {
return error_;
}
-FileOutputStream::FileOutputStream(const std::string& path, int mode, size_t buffer_capacity)
- : FileOutputStream(unique_fd(::android::base::utf8::open(path.c_str(), mode)),
- buffer_capacity) {
+FileOutputStream::FileOutputStream(const std::string& path, size_t buffer_capacity)
+ : buffer_capacity_(buffer_capacity) {
+ int mode = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
+ owned_fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode, 0666)));
+ fd_ = owned_fd_.get();
+ if (fd_ < 0) {
+ error_ = SystemErrorCodeToString(errno);
+ } else {
+ buffer_.reset(new uint8_t[buffer_capacity_]);
+ }
}
FileOutputStream::FileOutputStream(unique_fd fd, size_t buffer_capacity)
@@ -111,9 +126,9 @@ FileOutputStream::FileOutputStream(unique_fd fd, size_t buffer_capacity)
}
FileOutputStream::FileOutputStream(int fd, size_t buffer_capacity)
- : fd_(fd), buffer_capacity_(buffer_capacity), buffer_offset_(0u), total_byte_count_(0u) {
- if (fd_ == -1) {
- error_ = SystemErrorCodeToString(errno);
+ : fd_(fd), buffer_capacity_(buffer_capacity) {
+ if (fd_ < 0) {
+ error_ = "Bad File Descriptor";
} else {
buffer_.reset(new uint8_t[buffer_capacity_]);
}
diff --git a/tools/aapt2/io/FileStream.h b/tools/aapt2/io/FileStream.h
index 4ed1ad556f10..62d910f0b06a 100644
--- a/tools/aapt2/io/FileStream.h
+++ b/tools/aapt2/io/FileStream.h
@@ -22,7 +22,6 @@
#include <memory>
#include <string>
-#include "android-base/file.h" // for O_BINARY
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
@@ -55,15 +54,15 @@ class FileInputStream : public InputStream {
android::base::unique_fd fd_;
std::string error_;
std::unique_ptr<uint8_t[]> buffer_;
- size_t buffer_capacity_;
- size_t buffer_offset_;
- size_t buffer_size_;
- size_t total_byte_count_;
+ size_t buffer_capacity_ = 0u;
+ size_t buffer_offset_ = 0u;
+ size_t buffer_size_ = 0u;
+ size_t total_byte_count_ = 0u;
};
class FileOutputStream : public OutputStream {
public:
- explicit FileOutputStream(const std::string& path, int mode = O_RDWR | O_CREAT | O_BINARY,
+ explicit FileOutputStream(const std::string& path,
size_t buffer_capacity = kDefaultBufferCapacity);
// Does not take ownership of `fd`.
@@ -97,9 +96,9 @@ class FileOutputStream : public OutputStream {
int fd_;
std::string error_;
std::unique_ptr<uint8_t[]> buffer_;
- size_t buffer_capacity_;
- size_t buffer_offset_;
- size_t total_byte_count_;
+ size_t buffer_capacity_ = 0u;
+ size_t buffer_offset_ = 0u;
+ size_t total_byte_count_ = 0u;
};
} // namespace io
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index a6d58caa07b7..c0eaa8e08418 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -16,6 +16,7 @@
#include "io/FileStream.h"
+#include "android-base/file.h"
#include "android-base/macros.h"
#include "android-base/test_utils.h"
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index c93461a66899..8d91b0098c1f 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -23,6 +23,7 @@
#include "text/Utf8Iterator.h"
#include "util/Util.h"
+using ::aapt::text::Printer;
using ::aapt::text::Utf8Iterator;
using ::android::StringPiece;
@@ -109,23 +110,22 @@ void AnnotationProcessor::AppendNewLine() {
}
}
-void AnnotationProcessor::WriteToStream(const StringPiece& prefix, std::ostream* out) const {
+void AnnotationProcessor::Print(Printer* printer) const {
if (has_comments_) {
std::string result = comment_.str();
for (StringPiece line : util::Tokenize(result, '\n')) {
- *out << prefix << line << "\n";
+ printer->Println(line);
}
- *out << prefix << " */"
- << "\n";
+ printer->Println(" */");
}
if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
- *out << prefix << "@Deprecated\n";
+ printer->Println("@Deprecated");
}
for (const AnnotationRule& rule : sAnnotationRules) {
if (annotation_bit_mask_ & rule.bit_mask) {
- *out << prefix << rule.annotation << "\n";
+ printer->Println(rule.annotation);
}
}
}
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index a7bf73f50de5..ae7bdb0c3ae2 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -22,6 +22,8 @@
#include "androidfw/StringPiece.h"
+#include "text/Printer.h"
+
namespace aapt {
// Builds a JavaDoc comment from a set of XML comments.
@@ -61,8 +63,8 @@ class AnnotationProcessor {
void AppendNewLine();
- // Writes the comments and annotations to the stream, with the given prefix before each line.
- void WriteToStream(const android::StringPiece& prefix, std::ostream* out) const;
+ // Writes the comments and annotations to the Printer.
+ void Print(text::Printer* printer) const;
private:
std::stringstream comment_;
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 856f4ccbd7f0..69f49c8b97c3 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -16,8 +16,12 @@
#include "java/AnnotationProcessor.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+#include "text/Printer.h"
+using ::aapt::io::StringOutputStream;
+using ::aapt::text::Printer;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Not;
@@ -33,9 +37,11 @@ TEST(AnnotationProcessorTest, EmitsDeprecated) {
AnnotationProcessor processor;
processor.AppendComment(comment);
- std::stringstream result;
- processor.WriteToStream("", &result);
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@Deprecated"));
}
@@ -44,9 +50,11 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
AnnotationProcessor processor;
processor.AppendComment("@SystemApi This is a system API");
- std::stringstream result;
- processor.WriteToStream("", &result);
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi"));
EXPECT_THAT(annotations, Not(HasSubstr("@SystemApi")));
@@ -57,9 +65,11 @@ TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) {
AnnotationProcessor processor;
processor.AppendComment("@TestApi This is a test API");
- std::stringstream result;
- processor.WriteToStream("", &result);
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.TestApi"));
EXPECT_THAT(annotations, Not(HasSubstr("@TestApi")));
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index 0c57e7e06128..b692ccf7e52d 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -18,25 +18,27 @@
#include "androidfw/StringPiece.h"
+using ::aapt::text::Printer;
using ::android::StringPiece;
namespace aapt {
-void ClassMember::WriteToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
- processor_.WriteToStream(prefix, out);
+void ClassMember::Print(bool /*final*/, Printer* printer) const {
+ processor_.Print(printer);
}
void MethodDefinition::AppendStatement(const StringPiece& statement) {
statements_.push_back(statement.to_string());
}
-void MethodDefinition::WriteToStream(const StringPiece& prefix, bool final,
- std::ostream* out) const {
- *out << prefix << signature_ << " {\n";
+void MethodDefinition::Print(bool final, Printer* printer) const {
+ printer->Print(signature_).Println(" {");
+ printer->Indent();
for (const auto& statement : statements_) {
- *out << prefix << " " << statement << "\n";
+ printer->Println(statement);
}
- *out << prefix << "}";
+ printer->Undent();
+ printer->Print("}");
}
ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember> member) {
@@ -62,34 +64,32 @@ bool ClassDefinition::empty() const {
return true;
}
-void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final,
- std::ostream* out) const {
+void ClassDefinition::Print(bool final, Printer* printer) const {
if (empty() && !create_if_empty_) {
return;
}
- ClassMember::WriteToStream(prefix, final, out);
+ ClassMember::Print(final, printer);
- *out << prefix << "public ";
+ printer->Print("public ");
if (qualifier_ == ClassQualifier::kStatic) {
- *out << "static ";
+ printer->Print("static ");
}
- *out << "final class " << name_ << " {\n";
-
- std::string new_prefix = prefix.to_string();
- new_prefix.append(kIndent);
+ printer->Print("final class ").Print(name_).Println(" {");
+ printer->Indent();
for (const std::unique_ptr<ClassMember>& member : ordered_members_) {
// There can be nullptr members when a member is added to the ClassDefinition
// and takes precedence over a previous member with the same name. The overridden member is
// set to nullptr.
if (member != nullptr) {
- member->WriteToStream(new_prefix, final, out);
- *out << "\n";
+ member->Print(final, printer);
+ printer->Println();
}
}
- *out << prefix << "}";
+ printer->Undent();
+ printer->Print("}");
}
constexpr static const char* sWarningHeader =
@@ -100,11 +100,12 @@ constexpr static const char* sWarningHeader =
" * should not be modified by hand.\n"
" */\n\n";
-bool ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
- bool final, std::ostream* out) {
- *out << sWarningHeader << "package " << package << ";\n\n";
- def->WriteToStream("", final, out);
- return bool(*out);
+void ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
+ bool final, io::OutputStream* out) {
+ Printer printer(out);
+ printer.Print(sWarningHeader).Print("package ").Print(package).Println(";");
+ printer.Println();
+ def->Print(final, &printer);
}
} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 28a3489e71a4..fb11266f1761 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -17,7 +17,6 @@
#ifndef AAPT_JAVA_CLASSDEFINITION_H
#define AAPT_JAVA_CLASSDEFINITION_H
-#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -27,6 +26,7 @@
#include "Resource.h"
#include "java/AnnotationProcessor.h"
+#include "text/Printer.h"
#include "util/Util.h"
namespace aapt {
@@ -47,11 +47,10 @@ class ClassMember {
virtual const std::string& GetName() const = 0;
- // Writes the class member to the out stream. Subclasses should derive this method
+ // Writes the class member to the Printer. Subclasses should derive this method
// to write their own data. Call this base method from the subclass to write out
// this member's comments/annotations.
- virtual void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const;
+ virtual void Print(bool final, text::Printer* printer) const;
private:
AnnotationProcessor processor_;
@@ -71,11 +70,16 @@ class PrimitiveMember : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
- *out << prefix << "public static " << (final ? "final " : "") << "int " << name_ << "=" << val_
- << ";";
+ void Print(bool final, text::Printer* printer) const override {
+ using std::to_string;
+
+ ClassMember::Print(final, printer);
+
+ printer->Print("public static ");
+ if (final) {
+ printer->Print("final ");
+ }
+ printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
}
private:
@@ -100,12 +104,14 @@ class PrimitiveMember<std::string> : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
+ void Print(bool final, text::Printer* printer) const override {
+ ClassMember::Print(final, printer);
- *out << prefix << "public static " << (final ? "final " : "") << "String "
- << name_ << "=\"" << val_ << "\";";
+ printer->Print("public static ");
+ if (final) {
+ printer->Print("final ");
+ }
+ printer->Print("String ").Print(name_).Print("=\"").Print(val_).Print("\";");
}
private:
@@ -136,25 +142,27 @@ class PrimitiveArrayMember : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
+ void Print(bool final, text::Printer* printer) const override {
+ ClassMember::Print(final, printer);
- *out << prefix << "public static final int[] " << name_ << "={";
+ printer->Print("public static final int[] ").Print(name_).Print("={");
+ printer->Indent();
const auto begin = elements_.begin();
const auto end = elements_.end();
for (auto current = begin; current != end; ++current) {
if (std::distance(begin, current) % kAttribsPerLine == 0) {
- *out << "\n" << prefix << kIndent << kIndent;
+ printer->Println();
}
- *out << *current;
+ printer->Print(to_string(*current));
if (std::distance(current, end) > 1) {
- *out << ", ";
+ printer->Print(", ");
}
}
- *out << "\n" << prefix << kIndent << "};";
+ printer->Println();
+ printer->Undent();
+ printer->Print("};");
}
private:
@@ -187,8 +195,7 @@ class MethodDefinition : public ClassMember {
return false;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override;
+ void Print(bool final, text::Printer* printer) const override;
private:
DISALLOW_COPY_AND_ASSIGN(MethodDefinition);
@@ -201,8 +208,8 @@ enum class ClassQualifier { kNone, kStatic };
class ClassDefinition : public ClassMember {
public:
- static bool WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
- bool final, std::ostream* out);
+ static void WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
+ bool final, io::OutputStream* out);
ClassDefinition(const android::StringPiece& name, ClassQualifier qualifier, bool createIfEmpty)
: name_(name.to_string()), qualifier_(qualifier), create_if_empty_(createIfEmpty) {}
@@ -220,8 +227,7 @@ class ClassDefinition : public ClassMember {
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override;
+ void Print(bool final, text::Printer* printer) const override;
private:
DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 91cef642fc3f..9861770083a2 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -37,8 +37,10 @@
#include "java/ClassDefinition.h"
#include "process/SymbolTable.h"
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -230,7 +232,7 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
const StringPiece& package_name_to_generate,
ClassDefinition* out_class_def,
MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt) {
+ Printer* r_txt_printer) {
const std::string array_field_name = TransformToFieldName(name.entry);
std::unique_ptr<ResourceArrayMember> array_def =
util::make_unique<ResourceArrayMember>(array_field_name);
@@ -323,8 +325,8 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
}
- if (out_r_txt != nullptr) {
- *out_r_txt << "int[] styleable " << array_field_name << " {";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Print("int[] styleable ").Print(array_field_name).Print(" {");
}
// Add the ResourceIds to the array member.
@@ -332,16 +334,16 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
array_def->AddElement(id);
- if (out_r_txt != nullptr) {
+ if (r_txt_printer != nullptr) {
if (i != 0) {
- *out_r_txt << ",";
+ r_txt_printer->Print(",");
}
- *out_r_txt << " " << id;
+ r_txt_printer->Print(" ").Print(id.to_string());
}
}
- if (out_r_txt != nullptr) {
- *out_r_txt << " }\n";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Println(" }");
}
// Add the Styleable array to the Styleable class.
@@ -396,9 +398,9 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
attr_processor->AppendComment(
StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
- if (out_r_txt != nullptr) {
- *out_r_txt << StringPrintf("int styleable %s %d\n", sorted_attributes[i].field_name.data(),
- (int)i);
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Println(
+ StringPrintf("int styleable %s %zd", sorted_attributes[i].field_name.c_str(), i));
}
out_class_def->AddMember(std::move(index_member));
@@ -422,10 +424,12 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
const ResourceEntry& entry, ClassDefinition* out_class_def,
MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt) {
+ text::Printer* r_txt_printer) {
ResourceId real_id = id;
if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
id.package_id() > kAppPackageId) {
+ // Workaround for feature splits using package IDs > 0x7F.
+ // See b/37498913.
real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
}
@@ -456,8 +460,13 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
out_class_def->AddMember(std::move(resource_member));
- if (out_r_txt != nullptr) {
- *out_r_txt << "int " << name.type << " " << field_name << " " << real_id << "\n";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Print("int ")
+ .Print(to_string(name.type))
+ .Print(" ")
+ .Print(field_name)
+ .Print(" ")
+ .Println(real_id.to_string());
}
if (out_rewrite_method != nullptr) {
@@ -497,7 +506,7 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
const ResourceTableType& type,
ClassDefinition* out_type_class_def,
MethodDefinition* out_rewrite_method_def,
- std::ostream* out_r_txt) {
+ Printer* r_txt_printer) {
for (const auto& entry : type.entries) {
const Maybe<std::string> unmangled_name =
UnmangleResource(package.name, package_name_to_generate, *entry);
@@ -532,18 +541,18 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
static_cast<const Styleable*>(entry->values.front()->value.get());
ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
- out_rewrite_method_def, out_r_txt);
+ out_rewrite_method_def, r_txt_printer);
} else {
ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
- out_r_txt);
+ r_txt_printer);
}
}
return true;
}
-bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::ostream* out,
- std::ostream* out_r_txt) {
- return Generate(package_name_to_generate, package_name_to_generate, out);
+bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, OutputStream* out,
+ OutputStream* out_r_txt) {
+ return Generate(package_name_to_generate, package_name_to_generate, out, out_r_txt);
}
static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
@@ -556,11 +565,16 @@ static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations
}
bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
- const StringPiece& out_package_name, std::ostream* out,
- std::ostream* out_r_txt) {
+ const StringPiece& out_package_name, OutputStream* out,
+ OutputStream* out_r_txt) {
ClassDefinition r_class("R", ClassQualifier::kNone, true);
std::unique_ptr<MethodDefinition> rewrite_method;
+ std::unique_ptr<Printer> r_txt_printer;
+ if (out_r_txt != nullptr) {
+ r_txt_printer = util::make_unique<Printer>(out_r_txt);
+ }
+
// Generate an onResourcesLoaded() callback if requested.
if (options_.rewrite_callback_options) {
rewrite_method =
@@ -586,7 +600,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
- rewrite_method.get(), out_r_txt)) {
+ rewrite_method.get(), r_txt_printer.get())) {
return false;
}
@@ -595,7 +609,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
if (priv_type) {
if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
- rewrite_method.get(), out_r_txt)) {
+ rewrite_method.get(), r_txt_printer.get())) {
return false;
}
}
@@ -619,22 +633,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
}
AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
-
- if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out)) {
- return false;
- }
-
- out->flush();
-
- if (out_r_txt != nullptr) {
- out_r_txt->flush();
-
- if (!*out_r_txt) {
- error_ = android::base::SystemErrorCodeToString(errno);
- return false;
- }
- }
-
+ ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
return true;
}
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 2541749750a6..4992f077c566 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -17,16 +17,16 @@
#ifndef AAPT_JAVA_CLASS_GENERATOR_H
#define AAPT_JAVA_CLASS_GENERATOR_H
-#include <ostream>
#include <string>
#include "androidfw/StringPiece.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "androidfw/StringPiece.h"
+#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
+#include "text/Printer.h"
namespace aapt {
@@ -70,14 +70,14 @@ class JavaClassGenerator {
// All symbols technically belong to a single package, but linked libraries will
// have their names mangled, denoting that they came from a different package.
// We need to generate these symbols in a separate file. Returns true on success.
- bool Generate(const android::StringPiece& package_name_to_generate, std::ostream* out,
- std::ostream* out_r_txt = nullptr);
+ bool Generate(const android::StringPiece& package_name_to_generate, io::OutputStream* out,
+ io::OutputStream* out_r_txt = nullptr);
bool Generate(const android::StringPiece& package_name_to_generate,
- const android::StringPiece& output_package_name, std::ostream* out,
- std::ostream* out_r_txt = nullptr);
+ const android::StringPiece& output_package_name, io::OutputStream* out,
+ io::OutputStream* out_r_txt = nullptr);
- const std::string& getError() const;
+ const std::string& GetError() const;
static std::string TransformToFieldName(const android::StringPiece& symbol);
@@ -94,13 +94,13 @@ class JavaClassGenerator {
bool ProcessType(const android::StringPiece& package_name_to_generate,
const ResourceTablePackage& package, const ResourceTableType& type,
ClassDefinition* out_type_class_def, MethodDefinition* out_rewrite_method_def,
- std::ostream* out_r_txt);
+ text::Printer* r_txt_printer);
// Writes a resource to the R.java file, optionally writing out a rewrite rule for its package
// ID if `out_rewrite_method` is not nullptr.
void ProcessResource(const ResourceNameRef& name, const ResourceId& id,
const ResourceEntry& entry, ClassDefinition* out_class_def,
- MethodDefinition* out_rewrite_method, std::ostream* out_r_txt);
+ MethodDefinition* out_rewrite_method, text::Printer* r_txt_printer);
// Writes a styleable resource to the R.java file, optionally writing out a rewrite rule for
// its package ID if `out_rewrite_method` is not nullptr.
@@ -109,7 +109,7 @@ class JavaClassGenerator {
const Styleable& styleable,
const android::StringPiece& package_name_to_generate,
ClassDefinition* out_class_def, MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt);
+ text::Printer* r_txt_printer);
IAaptContext* context_;
ResourceTable* table_;
@@ -117,7 +117,7 @@ class JavaClassGenerator {
std::string error_;
};
-inline const std::string& JavaClassGenerator::getError() const {
+inline const std::string& JavaClassGenerator::GetError() const {
return error_;
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 668e4340e839..02f4cb14eb41 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -16,12 +16,13 @@
#include "java/JavaClassGenerator.h"
-#include <sstream>
#include <string>
+#include "io/StringStream.h"
#include "test/Test.h"
#include "util/Util.h"
+using ::aapt::io::StringOutputStream;
using ::android::StringPiece;
using ::testing::HasSubstr;
using ::testing::Lt;
@@ -45,7 +46,8 @@ TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string result;
+ StringOutputStream out(&result);
EXPECT_FALSE(generator.Generate("android", &out));
}
@@ -69,10 +71,10 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
EXPECT_TRUE(generator.Generate("android", &out));
-
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, HasSubstr("public static final int hey_man=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int[] hey_dude={"));
@@ -93,10 +95,12 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
+ out.Flush();
- std::string output = out.str();
EXPECT_THAT(output, HasSubstr("package com.android.internal;"));
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, Not(HasSubstr("two")));
@@ -117,10 +121,12 @@ TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
+ out.Flush();
- std::string output = out.str();
EXPECT_THAT(output, HasSubstr("public static final class attr"));
EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
}
@@ -147,9 +153,11 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, Not(HasSubstr("two")));
EXPECT_THAT(output, Not(HasSubstr("three")));
@@ -158,9 +166,11 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
EXPECT_THAT(output, Not(HasSubstr("three")));
@@ -169,9 +179,11 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
@@ -235,10 +247,11 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
EXPECT_TRUE(generator.Generate("android", &out));
+ out.Flush();
- std::string output = out.str();
EXPECT_THAT(output, HasSubstr("int foo_bar="));
EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
}
@@ -258,9 +271,11 @@ TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
const char* expected_text =
R"EOF(/**
@@ -298,9 +313,11 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
JavaClassGeneratorOptions options;
options.use_final = false;
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, HasSubstr("attr name android:one"));
EXPECT_THAT(output, HasSubstr("attr description"));
@@ -332,9 +349,11 @@ TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
JavaClassGeneratorOptions options;
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
std::string::size_type actionbar_pos = output.find("int[] ActionBar");
ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
@@ -373,9 +392,11 @@ TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
JavaClassGeneratorOptions options;
options.use_final = false;
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
EXPECT_THAT(output, Not(HasSubstr("@attr description")));
@@ -409,10 +430,10 @@ TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary)
options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
-
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, HasSubstr("void onResourcesLoaded"));
EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index b12202a8d137..3f6645facaa2 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -23,8 +23,7 @@
namespace aapt {
-std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
- xml::XmlResource* res);
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index ada563409d19..c324238d3ecb 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -16,8 +16,10 @@
#include "java/ManifestClassGenerator.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+using ::aapt::io::StringOutputStream;
using ::testing::HasSubstr;
using ::testing::Not;
@@ -144,12 +146,9 @@ static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xm
return ::testing::AssertionFailure() << "manifest_class == nullptr";
}
- std::stringstream out;
- if (!manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out)) {
- return ::testing::AssertionFailure() << "failed to write java file";
- }
-
- *out_str = out.str();
+ StringOutputStream out(out_str);
+ manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out);
+ out.Flush();
return ::testing::AssertionSuccess();
}
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b214d2169f50..132b234ab1e4 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -20,14 +20,18 @@
#include <string>
#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
#include "JavaClassGenerator.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
-#include "androidfw/StringPiece.h"
+#include "text/Printer.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+
namespace aapt {
namespace proguard {
@@ -326,12 +330,13 @@ bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set) {
return true;
}
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) {
+ Printer printer(out);
for (const auto& entry : keep_set.manifest_class_set_) {
for (const UsageLocation& location : entry.second) {
- *out << "# Referenced at " << location.source << "\n";
+ printer.Print("# Referenced at ").Println(location.source.to_string());
}
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
}
for (const auto& entry : keep_set.conditional_class_set_) {
@@ -342,26 +347,31 @@ bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
}
for (const UsageLocation& location : entry.second) {
- *out << "# Referenced at " << location.source << "\n";
+ printer.Print("# Referenced at ").Println(location.source.to_string());
}
if (keep_set.conditional_keep_rules_ && can_be_conditional) {
- *out << "-if class **.R$layout {\n";
+ printer.Println("-if class **.R$layout {");
+ printer.Indent();
for (const UsageLocation& location : locations) {
- auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
- *out << " int " << transformed_name << ";\n";
+ printer.Print("int ")
+ .Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
+ .Println(";");
}
- *out << "}\n";
+ printer.Undent();
+ printer.Println("}");
+ printer.Println();
}
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+ printer.Println();
}
for (const auto& entry : keep_set.method_set_) {
for (const UsageLocation& location : entry.second) {
- *out << "# Referenced at " << location.source << "\n";
+ printer.Print("# Referenced at ").Println(location.source.to_string());
}
- *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
+ printer.Print("-keepclassmembers class * { *** ").Print(entry.first).Println("(...); }");
+ printer.Println();
}
- return true;
}
bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 8dbe3c2bd1e8..46827ee7cf93 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -22,11 +22,13 @@
#include <set>
#include <string>
+#include "androidfw/StringPiece.h"
+
#include "Resource.h"
#include "ResourceTable.h"
#include "Source.h"
#include "ValueVisitor.h"
-#include "androidfw/StringPiece.h"
+#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
@@ -62,7 +64,7 @@ class KeepSet {
}
private:
- friend bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+ friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
@@ -76,11 +78,12 @@ class KeepSet {
bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set,
bool main_dex_only = false);
+
bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
-bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table,
- KeepSet* keep_set);
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
+
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 802c56a47de4..37d1a5fbaeb8 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -17,13 +17,23 @@
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+using ::aapt::io::StringOutputStream;
using ::testing::HasSubstr;
using ::testing::Not;
namespace aapt {
+std::string GetKeepSetString(const proguard::KeepSet& set) {
+ std::string out;
+ StringOutputStream sout(&out);
+ proguard::WriteKeepSet(set, &sout);
+ sout.Flush();
+ return out;
+}
+
TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
@@ -34,10 +44,8 @@ TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -50,10 +58,8 @@ TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -68,10 +74,8 @@ TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
}
@@ -87,10 +91,8 @@ TEST(ProguardRulesTest, CustomViewRulesAreEmitted) {
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -126,11 +128,10 @@ TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
ASSERT_TRUE(proguard::CollectProguardRules(bar_layout.get(), &set));
ASSERT_TRUE(proguard::CollectProguardRules(foo_layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -148,10 +149,9 @@ TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) {
set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
@@ -170,11 +170,10 @@ TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) {
set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, Not(HasSubstr("-if")));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
}
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -187,10 +186,8 @@ TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("bar_method"));
}
@@ -208,10 +205,8 @@ TEST(ProguardRulesTest, MenuRulesAreEmitted) {
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(menu.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("on_click"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
diff --git a/tools/aapt2/text/Printer.cpp b/tools/aapt2/text/Printer.cpp
index 38b3585f81f5..243800c9385f 100644
--- a/tools/aapt2/text/Printer.cpp
+++ b/tools/aapt2/text/Printer.cpp
@@ -26,18 +26,18 @@ using ::android::StringPiece;
namespace aapt {
namespace text {
-void Printer::Println(const StringPiece& str) {
+Printer& Printer::Println(const StringPiece& str) {
Print(str);
- Print("\n");
+ return Print("\n");
}
-void Printer::Println() {
- Print("\n");
+Printer& Printer::Println() {
+ return Print("\n");
}
-void Printer::Print(const StringPiece& str) {
+Printer& Printer::Print(const StringPiece& str) {
if (error_) {
- return;
+ return *this;
}
auto remaining_str_begin = str.begin();
@@ -53,7 +53,7 @@ void Printer::Print(const StringPiece& str) {
for (int i = 0; i < indent_level_; i++) {
if (!io::Copy(out_, " ")) {
error_ = true;
- return;
+ return *this;
}
}
needs_indent_ = false;
@@ -61,7 +61,7 @@ void Printer::Print(const StringPiece& str) {
if (!io::Copy(out_, str_to_copy)) {
error_ = true;
- return;
+ return *this;
}
}
@@ -69,7 +69,7 @@ void Printer::Print(const StringPiece& str) {
if (new_line_iter != remaining_str_end) {
if (!io::Copy(out_, "\n")) {
error_ = true;
- return;
+ return *this;
}
needs_indent_ = true;
// Ok to increment iterator here because we know that the '\n' character is one byte.
@@ -78,6 +78,7 @@ void Printer::Print(const StringPiece& str) {
remaining_str_begin = new_line_iter;
}
}
+ return *this;
}
void Printer::Indent() {
diff --git a/tools/aapt2/text/Printer.h b/tools/aapt2/text/Printer.h
index 94b3c0bf9449..f399f8ea5e0f 100644
--- a/tools/aapt2/text/Printer.h
+++ b/tools/aapt2/text/Printer.h
@@ -31,9 +31,9 @@ class Printer {
explicit Printer(::aapt::io::OutputStream* out) : out_(out) {
}
- void Print(const ::android::StringPiece& str);
- void Println(const ::android::StringPiece& str);
- void Println();
+ Printer& Print(const ::android::StringPiece& str);
+ Printer& Println(const ::android::StringPiece& str);
+ Printer& Println();
void Indent();
void Undent();
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index c9f3199ef616..fd42033d839c 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -72,6 +72,9 @@ class Field():
self.ident = self.raw.replace(" deprecated ", " ")
+ def __hash__(self):
+ return hash(self.raw)
+
def __repr__(self):
return self.raw
@@ -110,6 +113,9 @@ class Method():
ident = ident[:ident.index(" throws ")]
self.ident = ident
+ def __hash__(self):
+ return hash(self.raw)
+
def __repr__(self):
return self.raw
@@ -145,6 +151,9 @@ class Class():
self.name = self.fullname[self.fullname.rindex(".")+1:]
+ def __hash__(self):
+ return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)))
+
def __repr__(self):
return self.raw
@@ -256,6 +265,14 @@ def error(clazz, detail, rule, msg):
_fail(clazz, detail, True, rule, msg)
+noticed = {}
+
+def notice(clazz):
+ global noticed
+
+ noticed[clazz.fullname] = hash(clazz)
+
+
def verify_constants(clazz):
"""All static final constants must be FOO_NAME style."""
if re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -1203,6 +1220,9 @@ def verify_method_name_not_kotlin_operator(clazz):
def examine_clazz(clazz):
"""Find all style issues in the given class."""
+
+ notice(clazz)
+
if clazz.pkg.name.startswith("java"): return
if clazz.pkg.name.startswith("junit"): return
if clazz.pkg.name.startswith("org.apache"): return
@@ -1258,10 +1278,11 @@ def examine_clazz(clazz):
def examine_stream(stream):
"""Find all style issues in the given API stream."""
- global failures
+ global failures, noticed
failures = {}
+ noticed = {}
_parse_stream(stream, examine_clazz)
- return failures
+ return (failures, noticed)
def examine_api(api):
@@ -1338,6 +1359,8 @@ if __name__ == "__main__":
help="Disable terminal colors")
parser.add_argument("--allow-google", action='store_const', const=True,
help="Allow references to Google")
+ parser.add_argument("--show-noticed", action='store_const', const=True,
+ help="Show API changes noticed")
args = vars(parser.parse_args())
if args['no_color']:
@@ -1350,16 +1373,21 @@ if __name__ == "__main__":
previous_file = args['previous.txt']
with current_file as f:
- cur_fail = examine_stream(f)
+ cur_fail, cur_noticed = examine_stream(f)
if not previous_file is None:
with previous_file as f:
- prev_fail = examine_stream(f)
+ prev_fail, prev_noticed = examine_stream(f)
# ignore errors from previous API level
for p in prev_fail:
if p in cur_fail:
del cur_fail[p]
+ # ignore classes unchanged from previous API level
+ for k, v in prev_noticed.iteritems():
+ if k in cur_noticed and v == cur_noticed[k]:
+ del cur_noticed[k]
+
"""
# NOTE: disabled because of memory pressure
# look for compatibility issues
@@ -1371,6 +1399,12 @@ if __name__ == "__main__":
print
"""
+ if args['show_noticed'] and len(cur_noticed) != 0:
+ print "%s API changes noticed %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+ for f in sorted(cur_noticed.keys()):
+ print f
+ print
+
if len(cur_fail) != 0:
print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
for f in sorted(cur_fail):
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_linter.py
index dcb90e411d34..15d39fdfd6ea 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_linter.py
@@ -163,11 +163,14 @@ def assert_font_supports_all_of_chars(font, chars):
'U+%04X was not found in %s' % (char, font))
-def assert_font_supports_none_of_chars(font, chars):
+def assert_font_supports_none_of_chars(font, chars, fallbackName):
best_cmap = get_best_cmap(font)
for char in chars:
- assert char not in best_cmap, (
- 'U+%04X was found in %s' % (char, font))
+ if fallbackName:
+ assert char not in best_cmap, 'U+%04X was found in %s' % (char, font)
+ else:
+ assert char not in best_cmap, (
+ 'U+%04X was found in %s in fallback %s' % (char, font, fallbackName))
def assert_font_supports_all_sequences(font, sequences):
@@ -196,19 +199,21 @@ def check_hyphens(hyphens_dir):
class FontRecord(object):
- def __init__(self, name, scripts, variant, weight, style, font):
+ def __init__(self, name, scripts, variant, weight, style, fallback_for, font):
self.name = name
self.scripts = scripts
self.variant = variant
self.weight = weight
self.style = style
+ self.fallback_for = fallback_for
self.font = font
def parse_fonts_xml(fonts_xml_path):
- global _script_to_font_map, _fallback_chain
+ global _script_to_font_map, _fallback_chains, _all_fonts
_script_to_font_map = collections.defaultdict(set)
- _fallback_chain = []
+ _fallback_chains = {}
+ _all_fonts = []
tree = ElementTree.parse(fonts_xml_path)
families = tree.findall('family')
# Minikin supports up to 254 but users can place their own font at the first
@@ -225,10 +230,17 @@ def parse_fonts_xml(fonts_xml_path):
'No variant expected for LGC font %s.' % name)
assert langs is None, (
'No language expected for LGC fonts %s.' % name)
+ assert name not in _fallback_chains, 'Duplicated name entry %s' % name
+ _fallback_chains[name] = []
else:
assert variant in {None, 'elegant', 'compact'}, (
'Unexpected value for variant: %s' % variant)
+ for family in families:
+ name = family.get('name')
+ variant = family.get('variant')
+ langs = family.get('lang')
+
if langs:
langs = langs.split()
scripts = {lang_to_script(lang) for lang in langs}
@@ -247,17 +259,36 @@ def parse_fonts_xml(fonts_xml_path):
assert style in {'normal', 'italic'}, (
'Unknown style "%s"' % style)
+ fallback_for = child.get('fallbackFor')
+
+ assert not name or not fallback_for, (
+ 'name and fallbackFor cannot be present at the same time')
+ assert not fallback_for or fallback_for in _fallback_chains, (
+ 'Unknown fallback name: %s' % fallback_for)
+
index = child.get('index')
if index:
index = int(index)
- _fallback_chain.append(FontRecord(
+ record = FontRecord(
name,
frozenset(scripts),
variant,
weight,
style,
- (font_file, index)))
+ fallback_for,
+ (font_file, index))
+
+ _all_fonts.append(record)
+
+ if not fallback_for:
+ if not name or name == 'sans-serif':
+ for _, fallback in _fallback_chains.iteritems():
+ fallback.append(record)
+ else:
+ _fallback_chains[name].append(record)
+ else:
+ _fallback_chains[fallback_for].append(record)
if name: # non-empty names are used for default LGC fonts
map_scripts = {'Latn', 'Grek', 'Cyrl'}
@@ -274,7 +305,7 @@ def check_emoji_coverage(all_emoji, equivalent_emoji):
def get_emoji_font():
emoji_fonts = [
- record.font for record in _fallback_chain
+ record.font for record in _all_fonts
if 'Zsye' in record.scripts]
assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
return emoji_fonts[0]
@@ -318,35 +349,36 @@ def check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji):
def check_emoji_defaults(default_emoji):
missing_text_chars = _emoji_properties['Emoji'] - default_emoji
- emoji_font_seen = False
- for record in _fallback_chain:
- if 'Zsye' in record.scripts:
- emoji_font_seen = True
- # No need to check the emoji font
- continue
- # For later fonts, we only check them if they have a script
- # defined, since the defined script may get them to a higher
- # score even if they appear after the emoji font. However,
- # we should skip checking the text symbols font, since
- # symbol fonts should be able to override the emoji display
- # style when 'Zsym' is explicitly specified by the user.
- if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts):
- continue
+ for name, fallback_chain in _fallback_chains.iteritems():
+ emoji_font_seen = False
+ for record in fallback_chain:
+ if 'Zsye' in record.scripts:
+ emoji_font_seen = True
+ # No need to check the emoji font
+ continue
+ # For later fonts, we only check them if they have a script
+ # defined, since the defined script may get them to a higher
+ # score even if they appear after the emoji font. However,
+ # we should skip checking the text symbols font, since
+ # symbol fonts should be able to override the emoji display
+ # style when 'Zsym' is explicitly specified by the user.
+ if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts):
+ continue
- # Check default emoji-style characters
- assert_font_supports_none_of_chars(record.font, sorted(default_emoji))
+ # Check default emoji-style characters
+ assert_font_supports_none_of_chars(record.font, sorted(default_emoji), name)
- # Mark default text-style characters appearing in fonts above the emoji
- # font as seen
- if not emoji_font_seen:
- missing_text_chars -= set(get_best_cmap(record.font))
+ # Mark default text-style characters appearing in fonts above the emoji
+ # font as seen
+ if not emoji_font_seen:
+ missing_text_chars -= set(get_best_cmap(record.font))
- # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
- # webdings yet.
- missing_text_chars -= _chars_by_age['7.0']
- assert missing_text_chars == set(), (
- 'Text style version of some emoji characters are missing: ' +
- repr(missing_text_chars))
+ # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
+ # webdings yet.
+ missing_text_chars -= _chars_by_age['7.0']
+ assert missing_text_chars == set(), (
+ 'Text style version of some emoji characters are missing: ' +
+ repr(missing_text_chars))
# Setting reverse to true returns a dictionary that maps the values to sets of
@@ -626,8 +658,19 @@ def compute_expected_emoji():
return all_emoji, default_emoji, equivalent_emoji
+def check_compact_only_fallback():
+ for name, fallback_chain in _fallback_chains.iteritems():
+ for record in fallback_chain:
+ if record.variant == 'compact':
+ same_script_elegants = [x for x in fallback_chain
+ if x.scripts == record.scripts and x.variant == 'elegant']
+ assert same_script_elegants, (
+ '%s must be in elegant of %s as fallback of "%s" too' % (
+ record.font, record.scripts, record.fallback_for),)
+
+
def check_vertical_metrics():
- for record in _fallback_chain:
+ for record in _all_fonts:
if record.name in ['sans-serif', 'sans-serif-condensed']:
font = open_font(record.font)
assert font['head'].yMax == 2163 and font['head'].yMin == -555, (
@@ -646,11 +689,12 @@ def check_vertical_metrics():
def check_cjk_punctuation():
cjk_scripts = {'Hans', 'Hant', 'Jpan', 'Kore'}
cjk_punctuation = range(0x3000, 0x301F + 1)
- for record in _fallback_chain:
- if record.scripts.intersection(cjk_scripts):
- # CJK font seen. Stop checking the rest of the fonts.
- break
- assert_font_supports_none_of_chars(record.font, cjk_punctuation)
+ for name, fallback_chain in _fallback_chains.iteritems():
+ for record in fallback_chain:
+ if record.scripts.intersection(cjk_scripts):
+ # CJK font seen. Stop checking the rest of the fonts.
+ break
+ assert_font_supports_none_of_chars(record.font, cjk_punctuation, name)
def main():
@@ -661,6 +705,8 @@ def main():
fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
parse_fonts_xml(fonts_xml_path)
+ check_compact_only_fallback()
+
check_vertical_metrics()
hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index a910c628a9e6..468864b49d3f 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -23,6 +23,10 @@ cc_binary_host {
"Collation.cpp",
"main.cpp",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libstats_proto_host",
@@ -90,6 +94,10 @@ cc_library_shared {
name: "libstatslog",
generated_sources: ["statslog.cpp"],
generated_headers: ["statslog.h"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
export_generated_headers: ["statslog.h"],
shared_libs: [
"liblog",
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index eb29150fc9f9..cca12947d26e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -96,8 +96,6 @@ java_type_name(java_type_t type)
static int
write_stats_log_cpp(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
@@ -109,6 +107,8 @@ write_stats_log_cpp(FILE* out, const Atoms& atoms)
fprintf(out, "namespace android {\n");
fprintf(out, "namespace util {\n");
+ fprintf(out, "// the single event tag id for all stats logs\n");
+ fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
// Print write methods
fprintf(out, "\n");
@@ -117,7 +117,7 @@ write_stats_log_cpp(FILE* out, const Atoms& atoms)
int argIndex;
fprintf(out, "void\n");
- fprintf(out, "stats_write(int code");
+ fprintf(out, "stats_write(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -128,7 +128,8 @@ write_stats_log_cpp(FILE* out, const Atoms& atoms)
fprintf(out, "{\n");
argIndex = 1;
- fprintf(out, " android_log_event_list event(code);\n");
+ fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
+ fprintf(out, " event << code;\n");
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_STRING) {
@@ -157,8 +158,6 @@ write_stats_log_cpp(FILE* out, const Atoms& atoms)
static int
write_stats_log_header(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
@@ -208,8 +207,7 @@ write_stats_log_header(FILE* out, const Atoms& atoms)
fprintf(out, "//\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
signature != atoms.signatures.end(); signature++) {
-
- fprintf(out, "void stats_write(int code");
+ fprintf(out, "void stats_write(int32_t code ");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -229,8 +227,6 @@ write_stats_log_header(FILE* out, const Atoms& atoms)
static int
write_stats_log_java(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
@@ -395,8 +391,6 @@ jni_function_signature(const vector<java_type_t>& signature)
static int
write_stats_log_jni(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
diff --git a/vr/Android.mk b/vr/Android.mk
index 5b65d3fddba1..73e9f23ba067 100644
--- a/vr/Android.mk
+++ b/vr/Android.mk
@@ -18,6 +18,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := libdvr_loader
LOCAL_MODULE_OWNER := google
LOCAL_SRC_FILES := dvr_library_loader.cpp
+LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_SHARED_LIBRARY)
# Java platform library for vr stuff.
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 115b86d156e5..aa2c268cab88 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -113,6 +113,32 @@ public class DiscoverySessionCallback {
}
/**
+ * Called when a discovery (publish or subscribe) operation results in a
+ * service discovery. Called when a Subscribe service was configured with a range requirement
+ * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and/or
+ * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)}. A discovery will only be declared
+ * (i.e. this callback called) if the range of the publisher is within the specified distance
+ * constraints.
+ *
+ * @param peerHandle An opaque handle to the peer matching our discovery operation.
+ * @param serviceSpecificInfo The service specific information (arbitrary
+ * byte array) provided by the peer as part of its discovery
+ * configuration.
+ * @param matchFilter The filter which resulted in this service discovery. For
+ * {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED},
+ * {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE} discovery sessions this is the publisher's
+ * match filter. For {@link PublishConfig#PUBLISH_TYPE_SOLICITED},
+ * {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE} discovery sessions this
+ * is the subscriber's match filter.
+ * @param distanceMm The measured distance to the Publisher in mm.
+ * @hide
+ */
+ public void onServiceDiscoveredWithinRange(PeerHandle peerHandle,
+ byte[] serviceSpecificInfo, List<byte[]> matchFilter, int distanceMm) {
+ /* empty */
+ }
+
+ /**
* Called in response to
* {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}
* when a message is transmitted successfully - i.e. when it was received successfully by the
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
index 8ff38425d5e0..421a8af2073e 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
@@ -29,6 +29,8 @@ oneway interface IWifiAwareDiscoverySessionCallback
void onSessionTerminated(int reason);
void onMatch(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter);
+ void onMatchWithDistance(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter,
+ int distanceMm);
void onMessageSendSuccess(int messageId);
void onMessageSendFail(int messageId, int reason);
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index d018620719e2..e60f52f88f9e 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -29,6 +29,7 @@ import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Defines the configuration of a Aware publish session. Built using
@@ -81,14 +82,19 @@ public final class PublishConfig implements Parcelable {
public final boolean mEnableTerminateNotification;
/** @hide */
+ public final boolean mEnableRanging;
+
+ /** @hide */
public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
- int publishType, int ttlSec, boolean enableTerminateNotification) {
+ int publishType, int ttlSec, boolean enableTerminateNotification,
+ boolean enableRanging) {
mServiceName = serviceName;
mServiceSpecificInfo = serviceSpecificInfo;
mMatchFilter = matchFilter;
mPublishType = publishType;
mTtlSec = ttlSec;
mEnableTerminateNotification = enableTerminateNotification;
+ mEnableRanging = enableRanging;
}
@Override
@@ -103,7 +109,8 @@ public final class PublishConfig implements Parcelable {
+ (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
+ ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
+ ", mPublishType=" + mPublishType + ", mTtlSec=" + mTtlSec
- + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+ + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+ + ", mEnableRanging=" + mEnableRanging + "]";
}
@Override
@@ -119,6 +126,7 @@ public final class PublishConfig implements Parcelable {
dest.writeInt(mPublishType);
dest.writeInt(mTtlSec);
dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+ dest.writeInt(mEnableRanging ? 1 : 0);
}
public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() {
@@ -135,9 +143,10 @@ public final class PublishConfig implements Parcelable {
int publishType = in.readInt();
int ttlSec = in.readInt();
boolean enableTerminateNotification = in.readInt() != 0;
+ boolean enableRanging = in.readInt() != 0;
return new PublishConfig(serviceName, ssi, matchFilter, publishType,
- ttlSec, enableTerminateNotification);
+ ttlSec, enableTerminateNotification, enableRanging);
}
};
@@ -157,21 +166,14 @@ public final class PublishConfig implements Parcelable {
lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
&& mPublishType == lhs.mPublishType
&& mTtlSec == lhs.mTtlSec
- && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+ && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+ && mEnableRanging == lhs.mEnableRanging;
}
@Override
public int hashCode() {
- int result = 17;
-
- result = 31 * result + Arrays.hashCode(mServiceName);
- result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
- result = 31 * result + Arrays.hashCode(mMatchFilter);
- result = 31 * result + mPublishType;
- result = 31 * result + mTtlSec;
- result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
-
- return result;
+ return Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec,
+ mEnableTerminateNotification, mEnableRanging);
}
/**
@@ -226,6 +228,7 @@ public final class PublishConfig implements Parcelable {
private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
private int mTtlSec = 0;
private boolean mEnableTerminateNotification = true;
+ private boolean mEnableRanging = false;
/**
* Specify the service name of the publish session. The actual on-air
@@ -352,12 +355,35 @@ public final class PublishConfig implements Parcelable {
}
/**
+ * Configure whether the publish discovery session supports ranging and allows peers to
+ * measure distance to it. This API is used in conjunction with
+ * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and
+ * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
+ * maximum distance at which discovery will be triggered.
+ * <p>
+ * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
+ * device will be refused. If the peer has ranging enabled (using the
+ * {@link SubscribeConfig} APIs listed above, it will never discover this device.
+ *
+ * @param enable If true, ranging is supported on request of the peer.
+ *
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ *
+ * @hide
+ */
+ public Builder setRangingEnabled(boolean enable) {
+ mEnableRanging = enable;
+ return this;
+ }
+
+ /**
* Build {@link PublishConfig} given the current requests made on the
* builder.
*/
public PublishConfig build() {
return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
- mTtlSec, mEnableTerminateNotification);
+ mTtlSec, mEnableTerminateNotification, mEnableRanging);
}
}
}
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 4bf2fb6aa6fe..f6552a767f4f 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -29,6 +29,7 @@ import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Defines the configuration of a Aware subscribe session. Built using
@@ -79,15 +80,32 @@ public final class SubscribeConfig implements Parcelable {
public final boolean mEnableTerminateNotification;
/** @hide */
+ public final boolean mMinDistanceMmSet;
+
+ /** @hide */
+ public final int mMinDistanceMm;
+
+ /** @hide */
+ public final boolean mMaxDistanceMmSet;
+
+ /** @hide */
+ public final int mMaxDistanceMm;
+
+ /** @hide */
public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
- int subscribeType, int ttlSec,
- boolean enableTerminateNotification) {
+ int subscribeType, int ttlSec, boolean enableTerminateNotification,
+ boolean minDistanceMmSet, int minDistanceMm, boolean maxDistanceMmSet,
+ int maxDistanceMm) {
mServiceName = serviceName;
mServiceSpecificInfo = serviceSpecificInfo;
mMatchFilter = matchFilter;
mSubscribeType = subscribeType;
mTtlSec = ttlSec;
mEnableTerminateNotification = enableTerminateNotification;
+ mMinDistanceMm = minDistanceMm;
+ mMinDistanceMmSet = minDistanceMmSet;
+ mMaxDistanceMm = maxDistanceMm;
+ mMaxDistanceMmSet = maxDistanceMmSet;
}
@Override
@@ -102,7 +120,11 @@ public final class SubscribeConfig implements Parcelable {
+ (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
+ ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
+ ", mSubscribeType=" + mSubscribeType + ", mTtlSec=" + mTtlSec
- + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+ + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+ + ", mMinDistanceMm=" + mMinDistanceMm
+ + ", mMinDistanceMmSet=" + mMinDistanceMmSet
+ + ", mMaxDistanceMm=" + mMaxDistanceMm
+ + ", mMaxDistanceMmSet=" + mMaxDistanceMmSet + "]";
}
@Override
@@ -118,6 +140,10 @@ public final class SubscribeConfig implements Parcelable {
dest.writeInt(mSubscribeType);
dest.writeInt(mTtlSec);
dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+ dest.writeInt(mMinDistanceMm);
+ dest.writeInt(mMinDistanceMmSet ? 1 : 0);
+ dest.writeInt(mMaxDistanceMm);
+ dest.writeInt(mMaxDistanceMmSet ? 1 : 0);
}
public static final Creator<SubscribeConfig> CREATOR = new Creator<SubscribeConfig>() {
@@ -134,9 +160,14 @@ public final class SubscribeConfig implements Parcelable {
int subscribeType = in.readInt();
int ttlSec = in.readInt();
boolean enableTerminateNotification = in.readInt() != 0;
-
- return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType,
- ttlSec, enableTerminateNotification);
+ int minDistanceMm = in.readInt();
+ boolean minDistanceMmSet = in.readInt() != 0;
+ int maxDistanceMm = in.readInt();
+ boolean maxDistanceMmSet = in.readInt() != 0;
+
+ return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType, ttlSec,
+ enableTerminateNotification, minDistanceMmSet, minDistanceMm, maxDistanceMmSet,
+ maxDistanceMm);
}
};
@@ -152,23 +183,37 @@ public final class SubscribeConfig implements Parcelable {
SubscribeConfig lhs = (SubscribeConfig) o;
- return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
- lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
- && mSubscribeType == lhs.mSubscribeType
- && mTtlSec == lhs.mTtlSec
- && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+ if (!(Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(
+ mServiceSpecificInfo, lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter,
+ lhs.mMatchFilter) && mSubscribeType == lhs.mSubscribeType && mTtlSec == lhs.mTtlSec
+ && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+ && mMinDistanceMmSet == lhs.mMinDistanceMmSet
+ && mMaxDistanceMmSet == lhs.mMaxDistanceMmSet)) {
+ return false;
+ }
+
+ if (mMinDistanceMmSet && mMinDistanceMm != lhs.mMinDistanceMm) {
+ return false;
+ }
+
+ if (mMaxDistanceMmSet && mMaxDistanceMm != lhs.mMaxDistanceMm) {
+ return false;
+ }
+
+ return true;
}
@Override
public int hashCode() {
- int result = 17;
+ int result = Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mSubscribeType,
+ mTtlSec, mEnableTerminateNotification, mMinDistanceMmSet, mMaxDistanceMmSet);
- result = 31 * result + Arrays.hashCode(mServiceName);
- result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
- result = 31 * result + Arrays.hashCode(mMatchFilter);
- result = 31 * result + mSubscribeType;
- result = 31 * result + mTtlSec;
- result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
+ if (mMinDistanceMmSet) {
+ result = Objects.hash(result, mMinDistanceMm);
+ }
+ if (mMaxDistanceMmSet) {
+ result = Objects.hash(result, mMaxDistanceMm);
+ }
return result;
}
@@ -213,6 +258,17 @@ public final class SubscribeConfig implements Parcelable {
"Match filter longer than supported by device characteristics");
}
}
+
+ if (mMinDistanceMmSet && mMinDistanceMm < 0) {
+ throw new IllegalArgumentException("Minimum distance must be non-negative");
+ }
+ if (mMaxDistanceMmSet && mMaxDistanceMm < 0) {
+ throw new IllegalArgumentException("Maximum distance must be non-negative");
+ }
+ if (mMinDistanceMmSet && mMaxDistanceMmSet && mMaxDistanceMm <= mMinDistanceMm) {
+ throw new IllegalArgumentException(
+ "Maximum distance must be greater than minimum distance");
+ }
}
/**
@@ -225,6 +281,10 @@ public final class SubscribeConfig implements Parcelable {
private int mSubscribeType = SUBSCRIBE_TYPE_PASSIVE;
private int mTtlSec = 0;
private boolean mEnableTerminateNotification = true;
+ private boolean mMinDistanceMmSet = false;
+ private int mMinDistanceMm;
+ private boolean mMaxDistanceMmSet = false;
+ private int mMaxDistanceMm;
/**
* Specify the service name of the subscribe session. The actual on-air
@@ -350,13 +410,69 @@ public final class SubscribeConfig implements Parcelable {
}
/**
+ * Configure the minimum distance to a discovered publisher at which to trigger a discovery
+ * notification. I.e. discovery will only be triggered if we've found a matching publisher
+ * (based on the other criteria in this configuration) <b>and</b> the distance to the
+ * publisher is > the value specified in this API.
+ * <p>
+ * Can be used in conjunction with {@link #setMaxDistanceMm(int)} to specify a geo-fence,
+ * i.e. discovery with min < distance < max.
+ * <p>
+ * If this API is called, the subscriber requires ranging. In such a case, the publisher
+ * peer must enable ranging using
+ * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+ * never be triggered.
+ *
+ * @param minDistanceMm Minimum distance, in mm, to the publisher above which to trigger
+ * discovery.
+ *
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ *
+ * @hide
+ */
+ public Builder setMinDistanceMm(int minDistanceMm) {
+ mMinDistanceMm = minDistanceMm;
+ mMinDistanceMmSet = true;
+ return this;
+ }
+
+ /**
+ * Configure the maximum distance to a discovered publisher at which to trigger a discovery
+ * notification. I.e. discovery will only be triggered if we've found a matching publisher
+ * (based on the other criteria in this configuration) <b>and</b> the distance to the
+ * publisher is < the value specified in this API.
+ * <p>
+ * Can be used in conjunction with {@link #setMinDistanceMm(int)} to specify a geo-fence,
+ * i.e. discovery with min < distance < max.
+ * <p>
+ * If this API is called, the subscriber requires ranging. In such a case, the publisher
+ * peer must enable ranging using
+ * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+ * never be triggered.
+ *
+ * @param maxDistanceMm Maximum distance, in mm, to the publisher below which to trigger
+ * discovery.
+ *
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ *
+ * @hide
+ */
+ public Builder setMaxDistanceMm(int maxDistanceMm) {
+ mMaxDistanceMm = maxDistanceMm;
+ mMaxDistanceMmSet = true;
+ return this;
+ }
+
+ /**
* Build {@link SubscribeConfig} given the current requests made on the
* builder.
*/
public SubscribeConfig build() {
return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mMatchFilter,
- mSubscribeType, mTtlSec,
- mEnableTerminateNotification);
+ mSubscribeType, mTtlSec, mEnableTerminateNotification,
+ mMinDistanceMmSet, mMinDistanceMm, mMaxDistanceMmSet, mMaxDistanceMm);
}
}
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index ed6804d5c876..166da48ed90a 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -20,8 +20,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
-import android.annotation.SystemService;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
@@ -564,6 +564,7 @@ public class WifiAwareManager {
private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5;
private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
private static final int CALLBACK_MESSAGE_RECEIVED = 7;
+ private static final int CALLBACK_MATCH_WITH_DISTANCE = 8;
private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
@@ -618,7 +619,9 @@ public class WifiAwareManager {
case CALLBACK_SESSION_TERMINATED:
onProxySessionTerminated(msg.arg1);
break;
- case CALLBACK_MATCH: {
+ case CALLBACK_MATCH:
+ case CALLBACK_MATCH_WITH_DISTANCE:
+ {
List<byte[]> matchFilter = null;
byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2);
try {
@@ -629,9 +632,16 @@ public class WifiAwareManager {
+ new String(HexEncoding.encode(arg))
+ "' - cannot be parsed: e=" + e);
}
- mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
- msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
- matchFilter);
+ if (msg.what == CALLBACK_MATCH) {
+ mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+ matchFilter);
+ } else {
+ mOriginalCallback.onServiceDiscoveredWithinRange(
+ new PeerHandle(msg.arg1),
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+ matchFilter, msg.arg2);
+ }
break;
}
case CALLBACK_MESSAGE_SEND_SUCCESS:
@@ -684,21 +694,38 @@ public class WifiAwareManager {
mHandler.sendMessage(msg);
}
- @Override
- public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
- if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
-
+ private void onMatchCommon(int messageType, int peerId, byte[] serviceSpecificInfo,
+ byte[] matchFilter, int distanceMm) {
Bundle data = new Bundle();
data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
- Message msg = mHandler.obtainMessage(CALLBACK_MATCH);
+ Message msg = mHandler.obtainMessage(messageType);
msg.arg1 = peerId;
+ msg.arg2 = distanceMm;
msg.setData(data);
mHandler.sendMessage(msg);
}
@Override
+ public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
+ if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
+
+ onMatchCommon(CALLBACK_MATCH, peerId, serviceSpecificInfo, matchFilter, 0);
+ }
+
+ @Override
+ public void onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter,
+ int distanceMm) {
+ if (VDBG) {
+ Log.v(TAG, "onMatchWithDistance: peerId=" + peerId + ", distanceMm=" + distanceMm);
+ }
+
+ onMatchCommon(CALLBACK_MATCH_WITH_DISTANCE, peerId, serviceSpecificInfo, matchFilter,
+ distanceMm);
+ }
+
+ @Override
public void onMessageSendSuccess(int messageId) {
if (VDBG) Log.v(TAG, "onMessageSendSuccess");
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index 128d6c91e7b6..735e872eabdc 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -44,7 +44,7 @@ import java.util.List;
@SystemService(Context.WIFI_RTT2_SERVICE)
public class WifiRttManager {
private static final String TAG = "WifiRttManager";
- private static final boolean VDBG = true;
+ private static final boolean VDBG = false;
private final Context mContext;
private final IWifiRttManager mService;
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 1aeeee30c237..653fcff84c0c 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -19,20 +19,17 @@ package android.net.wifi.aware;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.wifi.RttManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.RemoteException;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
@@ -407,6 +404,7 @@ public class WifiAwareManagerTest {
final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data!
final int messageId = 2123;
final int reason = AWARE_STATUS_ERROR;
+ final int distanceMm = 100;
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
mockSubscribeSession);
@@ -442,6 +440,8 @@ public class WifiAwareManagerTest {
// (3) ...
subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+ sessionProxyCallback.getValue().onMatchWithDistance(peerHandle.peerId, string1.getBytes(),
+ matchFilter, distanceMm);
sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
@@ -450,7 +450,9 @@ public class WifiAwareManagerTest {
inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
- eq(string1.getBytes()), (List<byte[]>) isNull());
+ eq(string1.getBytes()), isNull());
+ inOrder.verify(mockSessionCallback).onServiceDiscoveredWithinRange(peerIdCaptor.capture(),
+ eq(string1.getBytes()), isNull(), eq(distanceMm));
assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
eq(string1.getBytes()));
@@ -685,11 +687,18 @@ public class WifiAwareManagerTest {
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+ collector.checkThat("mServiceSpecificInfo", subscribeConfig.mServiceSpecificInfo,
+ equalTo(null));
+ collector.checkThat("mMatchFilter", subscribeConfig.mMatchFilter, equalTo(null));
collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
collector.checkThat("mEnableTerminateNotification",
subscribeConfig.mEnableTerminateNotification, equalTo(true));
+ collector.checkThat("mMinDistanceCmSet", subscribeConfig.mMinDistanceMmSet, equalTo(false));
+ collector.checkThat("mMinDistanceMm", subscribeConfig.mMinDistanceMm, equalTo(0));
+ collector.checkThat("mMaxDistanceMmSet", subscribeConfig.mMaxDistanceMmSet, equalTo(false));
+ collector.checkThat("mMaxDistanceMm", subscribeConfig.mMaxDistanceMm, equalTo(0));
}
@Test
@@ -698,16 +707,19 @@ public class WifiAwareManagerTest {
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] matchFilter = { 1, 16, 1, 22 };
final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
- final int subscribeCount = 10;
final int subscribeTtl = 15;
final boolean enableTerminateNotification = false;
+ final int minDistance = 10;
+ final int maxDistance = 50;
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
- new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+ new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setSubscribeType(subscribeType)
.setTtlSec(subscribeTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setMinDistanceMm(minDistance)
+ .setMaxDistanceMm(maxDistance).build();
collector.checkThat("mServiceName", serviceName.getBytes(),
equalTo(subscribeConfig.mServiceName));
@@ -719,6 +731,10 @@ public class WifiAwareManagerTest {
collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
equalTo(subscribeConfig.mEnableTerminateNotification));
+ collector.checkThat("mMinDistanceMmSet", true, equalTo(subscribeConfig.mMinDistanceMmSet));
+ collector.checkThat("mMinDistanceMm", minDistance, equalTo(subscribeConfig.mMinDistanceMm));
+ collector.checkThat("mMaxDistanceMmSet", true, equalTo(subscribeConfig.mMaxDistanceMmSet));
+ collector.checkThat("mMaxDistanceMm", maxDistance, equalTo(subscribeConfig.mMaxDistanceMm));
}
@Test
@@ -729,13 +745,17 @@ public class WifiAwareManagerTest {
final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
final int subscribeTtl = 15;
final boolean enableTerminateNotification = true;
+ final int minDistance = 10;
+ final int maxDistance = 50;
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setSubscribeType(subscribeType)
.setTtlSec(subscribeTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setMinDistanceMm(minDistance)
+ .setMaxDistanceMm(maxDistance).build();
Parcel parcelW = Parcel.obtain();
subscribeConfig.writeToParcel(parcelW, 0);
@@ -769,11 +789,15 @@ public class WifiAwareManagerTest {
PublishConfig publishConfig = new PublishConfig.Builder().build();
collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+ collector.checkThat("mServiceSpecificInfo", publishConfig.mServiceSpecificInfo,
+ equalTo(null));
+ collector.checkThat("mMatchFilter", publishConfig.mMatchFilter, equalTo(null));
collector.checkThat("mPublishType", publishConfig.mPublishType,
equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
collector.checkThat("mEnableTerminateNotification",
publishConfig.mEnableTerminateNotification, equalTo(true));
+ collector.checkThat("mEnableRanging", publishConfig.mEnableRanging, equalTo(false));
}
@Test
@@ -782,16 +806,17 @@ public class WifiAwareManagerTest {
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] matchFilter = { 1, 16, 1, 22 };
final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
final int publishTtl = 15;
final boolean enableTerminateNotification = false;
+ final boolean enableRanging = true;
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setPublishType(publishType)
.setTtlSec(publishTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setRangingEnabled(enableRanging).build();
collector.checkThat("mServiceName", serviceName.getBytes(),
equalTo(publishConfig.mServiceName));
@@ -802,6 +827,7 @@ public class WifiAwareManagerTest {
collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
equalTo(publishConfig.mEnableTerminateNotification));
+ collector.checkThat("mEnableRanging", enableRanging, equalTo(publishConfig.mEnableRanging));
}
@Test
@@ -810,16 +836,17 @@ public class WifiAwareManagerTest {
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] matchFilter = { 1, 16, 1, 22 };
final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
final int publishTtl = 15;
final boolean enableTerminateNotification = false;
+ final boolean enableRanging = true;
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setPublishType(publishType)
.setTtlSec(publishTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setRangingEnabled(enableRanging).build();
Parcel parcelW = Parcel.obtain();
publishConfig.writeToParcel(parcelW, 0);